КулЛиб - Классная библиотека! Скачать книги бесплатно 

Git для профессионального программиста [Скотт Чакон] (pdf) читать онлайн

Книга в формате pdf! Изображения и текст могут не отображаться!


 [Настройки текста]  [Cбросить фильтры]
ББК 32.973.2-018
УДК 004.3
Д42

Чакон С., Штрауб Б.
Ч-16 Git для профессионального программиста. — СПб.: Питер, 2016. — 496 с.: ил. —
(Серия «Библиотека программиста»).
ISBN 978-5-496-01763-3
Эта книга представляет собой обновленное руководство по использованию Git в современных
условиях. С тех пор как проект Git — распределенная система управления версиями — был создан Линусом Торвальдсом, прошло много лет, и система Git превратилась в доминирующую систему контроля версий, как для коммерческих целей, так и для проектов с открытым исходным
кодом. Эффективный и хорошо реализованный контроль версий необходим для любого успешного
веб-проекта. Постепенно эту систему приняли на вооружение практически все сообщества разработчиков ПО с открытым исходным кодом. Появление огромного числа графических интерфейсов
для всех платформ и поддержка IDE позволили внедрить Git в операционные системы семейства
Windows. Второе издание книги было обновлено для Git-версии 2.0 и уделяет большое внимание
GitHub.

12+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)

ББК 32.973.2-018
УДК 004.3
Права на издание получены по соглашению с Apress.
Все права защищены. Никакая часть данной книги не может быть воспроизведена в какой бы то ни было форме
без письменного разрешения владельцев авторских прав.
Информация, содержащаяся в данной книге, получена из источников, рассматриваемых издательством как надежные. Тем не менее, имея в виду возможные человеческие или технические ошибки, издательство не может
гарантировать абсолютную точность и полноту приводимых сведений и не несет ответственности за возможные
ошибки, связанные с использованием книги.
ISBN 978-1484200773 англ.
978-5-496-01763-3

© Apress
© Перевод на русский язык ООО Издательство «Питер», 2016
© Издание на русском языке, оформление ООО Издательство «Питер», 2016
© Серия «Библиотека программиста», 2016

Краткое содержание

Предисловие от Скотта Чакона.......................................................................... 16
Предисловие от Бена Страуба............................................................................ 18
1. Начало работы............................................................................................... 19
2. Основы Git...................................................................................................... 34
3. Ветвления в Git.............................................................................................. 67
4. Git на сервере............................................................................................... 105
5. Распределенная система Git....................................................................... 129
6. GitHub........................................................................................................... 169
7. Git-инструментарий..................................................................................... 221
8. Настройка системы Git................................................................................ 328
9. Git и другие системы контроля версий...................................................... 361
10. Git изнутри................................................................................................... 418
Приложение A. Git в других средах.................................................................. 458
Приложение Б. Встраивание Git в приложения.............................................. 471
Приложение В. Git-команды............................................................................. 478
Об авторах.......................................................................................................... 495

Содержание

Предисловие от Скотта Чакона.......................................................................... 16
Предисловие от Бена Страуба............................................................................ 18
1. Начало работы................................................................................................. 19
Управление версиями............................................................................................. 19
Локальные системы контроля версий................................................................. 20
Централизованные системы контроля версий..................................................... 20
Распределенные системы контроля версий........................................................ 21
Краткая история Git................................................................................................ 23
Основы Git.............................................................................................................. 23
Снимки состояний, а не изменений.................................................................... 24
Локальность операций....................................................................................... 25
Целостность Git.................................................................................................. 26
Git, как правило, только добавляет данные........................................................ 26
Три состояния.................................................................................................... 26
Командная строка................................................................................................... 28
Установка Git.......................................................................................................... 28
Установка в Linux............................................................................................... 29
Установка в Mac................................................................................................. 29
Установка в Windows.......................................................................................... 30
Первая настройка Git.............................................................................................. 31
Ваш идентификатор........................................................................................... 31

Краткое содержание   7

Выбор редактора................................................................................................ 32
Проверка настроек............................................................................................. 32
Получение справочной информации....................................................................... 33
Заключение............................................................................................................ 33
2. Основы Git........................................................................................................ 34
Создание репозитория в Git.................................................................................... 34
Инициализация репозитория в существующей папке.......................................... 34
Клонирование существующего репозитория....................................................... 35
Запись изменений в репозиторий........................................................................... 36
Проверка состояния файлов............................................................................... 37
Слежение за новыми файлами........................................................................... 37
Индексация измененных файлов........................................................................ 38
Краткий отчет о состоянии................................................................................. 39
Игнорирование файлов...................................................................................... 40
Просмотр индексированных и неиндексированных изменений........................... 41
Фиксация изменений.......................................................................................... 43
Пропуск области индексирования....................................................................... 45
Удаление файлов............................................................................................... 45
Перемещение файлов........................................................................................ 47
Просмотр истории версий....................................................................................... 47
Ограничение вывода команды log...................................................................... 52
Отмена изменений.................................................................................................. 54
Отмена индексирования..................................................................................... 55
Отмена внесенных в файл изменений................................................................ 56
Удаленные репозитории......................................................................................... 57
Отображение удаленных репозиториев.............................................................. 57
Добавление удаленных репозиториев................................................................ 58
Извлечение данных из удаленных репозиториев................................................ 59
Отправка данных в удаленный репозиторий....................................................... 59
Просмотр удаленных репозиториев.................................................................... 60
Удаление и переименование удаленных репозиториев....................................... 61
Теги....................................................................................................................... 61
Вывод списка тегов............................................................................................ 62
Создание тегов................................................................................................... 62
Теги с комментариями........................................................................................ 62
Легковесные теги............................................................................................... 63
Расстановка тегов постфактум........................................................................... 63
Обмен тегами..................................................................................................... 64
Псевдонимы в Git................................................................................................... 65
Заключение............................................................................................................ 66

8    Краткое содержание
3. Ветвления в Git................................................................................................ 67
Суть ветвления....................................................................................................... 67
Создание новой ветки........................................................................................ 70
Смена веток....................................................................................................... 71
Основы ветвления и слияния.................................................................................. 74
Основы ветвления.............................................................................................. 74
Основы слияния................................................................................................. 78
Конфликты при слиянии..................................................................................... 80
Управление ветками............................................................................................... 82
Приемы работы с ветками...................................................................................... 83
Долгоживущие ветки.......................................................................................... 84
Тематические ветки........................................................................................... 85
Удаленные ветки.................................................................................................... 87
Отправка данных............................................................................................... 91
Слежение за ветками......................................................................................... 92
Получение данных с последующим слиянием..................................................... 94
Ликвидация веток с удаленного сервера............................................................ 94
Перемещение данных............................................................................................. 94
Основы перемещения данных............................................................................ 94
Более интересные варианты перемещений........................................................ 97
Риски, связанные с перемещением..................................................................... 99
Перемещение после перемещения................................................................... 102
Сравнение перемещения и слияния.................................................................. 103
Заключение.......................................................................................................... 104
4. Git на сервере................................................................................................. 105
Протоколы............................................................................................................ 106
Локальный протокол........................................................................................ 106
Протоколы семейства HTTP.............................................................................. 107
Протокол SSH................................................................................................... 110
Протокол Git..................................................................................................... 111
Настройка Git на сервере...................................................................................... 111
Размещение на сервере голого репозитория.................................................... 112
Простые настройки........................................................................................... 113
Создание открытого ключа SSH............................................................................ 114
Настройка сервера............................................................................................... 115
Git-демон.............................................................................................................. 117
Интеллектуальный протокол HTTP........................................................................ 119
Интерфейс GitWeb................................................................................................ 120
Приложение GitLab............................................................................................... 122

Краткое содержание   9

Установка......................................................................................................... 122
Администрирование......................................................................................... 123
Пользователи................................................................................................... 124
Группы............................................................................................................. 124
Проекты........................................................................................................... 125
Хуки................................................................................................................. 126
Базовое применение........................................................................................ 126
Совместная работа........................................................................................... 126
Сторонний хостинг............................................................................................... 127
Заключение.......................................................................................................... 128
5. Распределенная система Git......................................................................... 129
Распределенные рабочие процессы...................................................................... 129
Централизованная работа................................................................................ 130
Диспетчер интеграции..................................................................................... 131
Диктатор и помощники..................................................................................... 132
Заключение...................................................................................................... 133
Содействие проекту.............................................................................................. 133
Рекомендации по созданию коммитов.............................................................. 134
Работа в маленькой группе.............................................................................. 136
Маленькая группа с руководителем.................................................................. 142
Открытый проект, ветвление............................................................................ 146
Открытый проект, электронная почта.............................................................. 150
Заключение...................................................................................................... 153
Сопровождение проекта....................................................................................... 153
Работа с тематическими ветками...................................................................... 153
Исправления, присланные по почте................................................................. 154
Просмотр вносимых изменений........................................................................ 158
Интеграция чужих наработок........................................................................... 159
Схема с большим количеством слияний............................................................ 162
Схема с перемещением и отбором.................................................................... 163
Программный компонент rerere........................................................................ 165
Идентификация устойчивых версий.................................................................. 165
Генерация номера сборки................................................................................ 166
Подготовка устойчивой версии......................................................................... 167
Команда shortlog.............................................................................................. 167
Заключение.......................................................................................................... 168
6. GitHub............................................................................................................. 169
Настройка и конфигурирование учетной записи................................................... 170
Доступ по протоколу SSH................................................................................. 170
Аватар.............................................................................................................. 172

10    Краткое содержание
Адреса электронной почты............................................................................... 173
Аутентификация по двум признакам................................................................ 173
Содействие проекту.............................................................................................. 174
Ветвления проектов......................................................................................... 175
Схема работы с GitHub..................................................................................... 175
Запрос на включение....................................................................................... 176
Стадии обработки запроса на включение......................................................... 180
Более сложные запросы на включение............................................................. 183
Язык разметки Markdown.................................................................................. 188
GitHub-версия языка Markdown......................................................................... 188
Сопровождение проекта....................................................................................... 193
Создание нового репозитория.......................................................................... 193
Добавление соавторов..................................................................................... 194
Управление запросами на включение............................................................... 195
Упоминания и уведомления.............................................................................. 201
Специальные файлы........................................................................................ 204
Администрирование проекта............................................................................ 206
Управление организацией.................................................................................... 207
Основные сведения об организации................................................................. 207
Группы............................................................................................................. 208
Журнал регистрации........................................................................................ 210
GitHub-сценарии................................................................................................... 210
Хуки................................................................................................................. 211
API для GitHub.................................................................................................. 214
От пользователя Octokit................................................................................... 220
Заключение.......................................................................................................... 220
7. Git-инструментарий....................................................................................... 221
Выбор версии....................................................................................................... 221
Одна версия..................................................................................................... 221
Сокращения журнала ссылок............................................................................ 224
Диапазоны коммитов........................................................................................ 226
Интерактивное индексирование........................................................................... 229
Индексирование файлов и его отмена.............................................................. 229
Индексирование изменений............................................................................. 231
Скрытие и очистка................................................................................................ 232
Скрытие вашей работы..................................................................................... 233
Более сложные варианты скрытия.................................................................... 235
Отмена скрытых изменений.............................................................................. 236
Создание ветки из скрытого фрагмента............................................................ 236
Очистка рабочей папки.................................................................................... 237

Краткое содержание   11

Подпись................................................................................................................ 238
Знакомство с GPG............................................................................................. 239
Подпись тегов.................................................................................................. 239
Проверка тегов................................................................................................ 240
Подпись коммитов............................................................................................ 240
Подпись должна быть у всех............................................................................ 242
Поиск................................................................................................................... 242
Команда git grep............................................................................................... 242
Поиск в Git-журнале......................................................................................... 244
Поиск по строкам кода..................................................................................... 244
Перезапись истории............................................................................................. 245
Редактирование последнего коммита............................................................... 246
Редактирование нескольких сообщений фиксации........................................... 246
Изменение порядка следования коммитов........................................................ 248
Объединение коммитов.................................................................................... 249
Разбиение коммита.......................................................................................... 250
Последнее средство: команда filter-branch........................................................ 251
Команда reset....................................................................................................... 252
Три дерева....................................................................................................... 253
Рабочий процесс.............................................................................................. 254
Роль команды reset.......................................................................................... 259
Команда reset с указанием пути....................................................................... 263
Объединение коммитов.................................................................................... 265
Сравнение с командой checkout........................................................................ 267
Заключение...................................................................................................... 269
Более сложные варианты слияния........................................................................ 269
Конфликты слияния......................................................................................... 270
Прерывание слияния........................................................................................ 272
Игнорирование пробелов................................................................................. 272
Слияние файлов вручную................................................................................. 273
Применение команды checkout......................................................................... 276
Протоколирование слияния.............................................................................. 277
Комбинированный формат............................................................................... 278
Отмена результатов слияния............................................................................ 280
Другие типы слияния....................................................................................... 284
Команда rerere..................................................................................................... 287
Отладка с помощью Git......................................................................................... 292
Примечания к файлам...................................................................................... 293
Двоичный поиск............................................................................................... 294
Подмодули........................................................................................................... 296
Начало работы................................................................................................. 296
Клонирование проекта с подмодулями............................................................. 298

12    Краткое содержание
Работа над проектом с подмодулями................................................................ 300
Публикация результатов редактирования подмодуля....................................... 305
Слияние результатов редактирования подмодуля............................................ 306
Полезные советы............................................................................................. 309
Пакеты................................................................................................................. 313
Замена................................................................................................................. 316
Хранение учетных данных.................................................................................... 322
Взгляд изнутри................................................................................................. 323
Нестандартный вариант хранения учетных данных.......................................... 325
Заключение.......................................................................................................... 327
8. Настройка системы Git.................................................................................. 328
Конфигурирование системы Git............................................................................ 328
Основные настройки на стороне клиента......................................................... 329
Цвета в Git....................................................................................................... 331
Внешние инструменты для слияния и индикации изменений............................ 332
Форматирование и пробелы............................................................................. 336
Настройка сервера........................................................................................... 338
Git-атрибуты......................................................................................................... 339
Бинарные файлы.............................................................................................. 339
Развертывание ключа...................................................................................... 342
Экспорт репозитория........................................................................................ 345
Стратегии слияния........................................................................................... 346
Git-хуки................................................................................................................ 347
Установка хука................................................................................................. 347
Хуки на стороне клиента.................................................................................. 347
Хуки для работы с коммитами.......................................................................... 347
Хуки для работы с электронной почтой............................................................ 348
Другие клиентские хуки................................................................................... 349
Хуки на стороне сервера.................................................................................. 350
Пример принудительного внедрения политики..................................................... 351
Хук на стороне сервера.................................................................................... 351
Формат сообщения фиксации........................................................................... 352
Система контроля доступа пользователей........................................................ 353
Тестирование................................................................................................... 356
Хуки на стороне клиента.................................................................................. 357
Заключение.......................................................................................................... 360
9. Git и другие системы контроля версий........................................................ 361
Git в качестве клиента.......................................................................................... 361
Git и Subversion................................................................................................ 362
Git и Mercurial................................................................................................... 372

Краткое содержание   13

Git и Perforce.................................................................................................... 380
Git и TFS........................................................................................................... 394
Переход на Git...................................................................................................... 404
Subversion........................................................................................................ 404
Mercurial........................................................................................................... 406
Perforce............................................................................................................ 408
TFS................................................................................................................... 410
Другие варианты импорта................................................................................ 411
Заключение.......................................................................................................... 417
10. Git изнутри................................................................................................... 418
Канализация и фарфор........................................................................................ 419
Объекты в Git....................................................................................................... 420
Объекты-деревья............................................................................................. 421
Объекты-коммиты............................................................................................ 424
Хранение объектов........................................................................................... 427
Ссылки в Git......................................................................................................... 428
Указатель HEAD................................................................................................ 429
Теги................................................................................................................. 430
Удаленные ветки.............................................................................................. 431
Pack-файлы.......................................................................................................... 432
Спецификация ссылок.......................................................................................... 435
Спецификация ссылок для отправки данных на сервер.................................... 437
Ликвидация ссылок.......................................................................................... 437
Протоколы передачи данных................................................................................ 438
Простой протокол............................................................................................ 438
Интеллектуальный протокол............................................................................ 440
Обслуживание репозитория и восстановление данных......................................... 444
Обслуживание репозитория............................................................................. 444
Восстановление данных................................................................................... 445
Удаление объектов........................................................................................... 447
Переменные среды............................................................................................... 451
Глобальное поведение..................................................................................... 451
Расположение репозитория.............................................................................. 451
Пути доступа.................................................................................................... 452
Фиксация изменений........................................................................................ 453
Работа в сети................................................................................................... 453
Определение изменений и слияние.................................................................. 454
Отладка........................................................................................................... 454
Разное.............................................................................................................. 456
Заключение.......................................................................................................... 457

14    Краткое содержание
Приложение A. Git в других средах.................................................................. 458
Графические интерфейсы..................................................................................... 458
Утилиты gitk и git-gui........................................................................................ 459
GitHub-клиенты для Mac и Windows.................................................................. 461
Подводя итоги.................................................................................................. 464
Другие GUI........................................................................................................... 464
Git в Visual Studio.................................................................................................. 465
Git в Eclipse........................................................................................................... 466
Git в Bash............................................................................................................. 466
Git в Zsh............................................................................................................... 468
Git в Powershell..................................................................................................... 469
Заключение.......................................................................................................... 470
Приложение Б. Встраивание Git в приложения.............................................. 471
Командная строка................................................................................................. 471
Libgit2................................................................................................................... 472
Нетривиальная функциональность................................................................... 474
Другие привязки.............................................................................................. 476
Приложение В. Git-команды............................................................................. 478
Настройка и конфигурирование............................................................................ 478
Копирование и создание проектов....................................................................... 479
Фиксация состояния............................................................................................. 480
Ветвления и слияния............................................................................................ 483
Совместная работа и обновление проектов.......................................................... 486
Проверка и сравнение.......................................................................................... 489
Отладка................................................................................................................ 490
Исправления........................................................................................................ 490
Электронная почта............................................................................................... 491
Внешние системы................................................................................................. 492
Администрирование............................................................................................. 493
Служебные команды............................................................................................. 494
Об авторах.......................................................................................................... 495

Эту книгу я посвящаю своим девочкам: жене Джессике, которая
поддерживала меня все эти годы, и дочери Джозефине, которая
будет поддерживать меня, когда я состарюсь и перестану понимать, что вокруг происходит.
Скотт
Моей жене Бекки, без которой это приключение могло закончиться, даже не начавшись.
Бен

Предисловие
от Скотта Чакона

Перед вами второе издание книги. Первая версия увидела свет четыре года назад.
Многое изменилось с того времени, хотя наиболее важные вещи остались незыблемыми. Большинство ключевых команд и концепций до сих пор применимы, ведь
Git-разработчики прилагают массу усилий для поддержания обратной совместимости, но появились и значительные нововведения и изменения в окружающем
систему Git сообществе. Второе издание книги предусматривает рассказ об этих
изменениях и обновлениях, что поможет новым пользователям быстрее войти
в курс дела.
На момент написания первой книги система Git была относительно сложной и по
сути представляла собой инструмент, ориентированный на опытного разработчика.
В некоторых сообществах она начала набирать популярность, но до повсеместного
ее использования, которое мы наблюдаем в наши дни, было далеко. Тем не менее,
постепенно эту систему приняли на вооружение практически все сообщества разработчиков ПО с открытым исходным кодом. Появление огромного числа графических интерфейсов для всех платформ и поддержка IDE позволили внедрить Git
в операционные системы семейства Windows. В первом издании книги об этом не
было и речи. Одной из основных целей нового издания является рассмотрение
всех этих новшеств.
Резко возросло и количество разработчиков ПО с открытым исходным кодом, которые пользуются системой Git. Почти пять лет назад начало работы над первой
версией книги (а процесс написания занял некоторое время) совпало с началом моей
работы в малоизвестной компании, занимающейся созданием сайта для хостинга
Git. Этот сайт назывался GitHub. К моменту публикации книги сайтом пользовалась

Предисловие от Скотта Чакона   17

от силы тысяча человек, а работали над ним только мы вчетвером. На момент же
написания этого предисловия сайт GitHub объявил о размещении 10-миллионного
проекта. Число учетных записей зарегистрированных на нем разработчиков достигло
почти 5 миллионов, а количество сотрудников превысило 230. Нравится вам это
или нет, но проект GitHub повлиял на сообщество разработчиков ПО с открытым
исходным кодом в такой степени, какую я не мог даже вообразить, когда начинал
трудиться над первой книгой.
В исходной версии книги я посвятил сайту GitHub небольшой раздел, описав его
как место для хостинга системы Git. Мне не очень нравилось, что, по сути, я пишу
про общественный ресурс, попутно рассказывая о своей роли в его создании. Мне
до сих пор не нравится этот конфликт интересов, но важность проекта GitHub в
Git-сообществе давно уже неоспорима. Поэтому теперь я решил не ограничиваться
примером хостинга системы Git, а выделить целую главу под детальное описание
проекта GitHub и способов эффективного его применения. Если вы собираетесь
изучать Git, умение работать с сайтом GitHub поможет вам влиться в огромное
сообщество, полезное вне зависимости от того, какой хостинг вы используете для
хранения своего кода.
Другим значительным изменением с момента предыдущей публикации стала разработка и растущее использование протокола HTTP для сетевых Git-операций.
В большинстве приведенных в книге примеров вместо протокола SSH фигурирует
более простой протокол HTTP.
Удивительно наблюдать за тем, как за последние несколько лет система Git развилась из практически неизвестной в доминирующую систему контроля версий,
причем как для коммерческих целей, так и для проектов с открытым исходным
кодом.
Надеюсь, вы получите удовольствие от чтения новой версии книги.

Предисловие
от Бена Страуба

Мой интерес к системе Git вызвало именно первое издание этой книги. Я узнал
способ создания программного обеспечения, более естественный, чем все, что я
встречал раньше. К этому моменту я был разработчиком уже несколько лет, но это
событие помогло мне свернуть на куда более интересную дорогу.
Теперь, годы спустя, я являюсь соавтором одной из основных реализаций Git,
­я работал и работаю в крупнейшей компании хостинга системы Git, обучая людей
по всему миру пользоваться ею. Когда Скотт спросил, не хочу ли я принять участие
в подготовке второго издания книги, я, не раздумывая, согласился.
Это большая честь для меня, и в процессе работы я получил огромное удовольствие.
Надеюсь, эта книга поможет вам в той же степени, в которой она помогла мне.

1

Начало работы

Эта глава поможет вам начать работу с системой Git. Первым делом мы предоставим общие сведения об инструментах контроля версий, затем поговорим о том,
как запустить Git в вашей операционной системе, и закончим описанием процесса
настройки. К концу этой главы вы поймете, зачем нужна система Git, почему ею
следует пользоваться, и будете готовы приступить к работе.

Управление версиями
Что такое «управление версиями» и почему оно должно нас интересовать? Система
управления версиями, или контроля версий, записывает историю изменения файла
или набора файлов, чтобы в будущем была возможность вернуться к конкретной
версии. В книге процесс управления версиями будет рассматриваться на фрагментах
кода, в реальности же эти операции можно проделывать с файлами практически
всех типов.
Графическим дизайнерам и веб-дизайнерам, которым нужно хранить все версии
изображений или компоновок, можно порекомендовать систему контроля версий
(Version Control System, VCS). Она позволяет возвращать в предыдущее состояние как отдельные файлы, так и целый проект, сравнивать сделанные изменения,
смотреть, кто и когда последним редактировал файл, являющийся источником
проблемы, и многое другое. Наличие VCS в общем случае означает, что при сбое
или потере файлов вы можете легко вернуться в рабочее состояние. И это вам
практически ничего не будет стоить.

20    Глава 1 • Начало работы

Локальные системы контроля версий
Многие люди в качестве метода контроля версий выбирают простое копирование файлов в другую папку (в лучшем случае такие папки помечаются метками
времени). Этот подход крайне популярен в силу своей простоты, но при этом он
потрясающе ненадежен. Легко забыть, в какой папке вы работаете, и случайно сделать запись не в тот файл или скопировать вовсе не те файлы, которые вы хотели.
Для урегулирования этого вопроса программисты давно изобрели локальные
системы контроля версий, снабженные простой базой данных, хранящей всю информацию об изменениях файлов (рис. 1.1).
ЛОКАЛЬНЫЙ КОМПЬЮТЕР
ВЫГРУЗКА

Файл

БАЗА ДАННЫХ ВЕРСИЙ

Версия 3

Версия 2

Версия 1

Рис. 1.1. Локальное управление версиями

Одной из наиболее популярных локальных систем контроля версий была система
контроля редакций (Revision Control System, RCS), которая до сих пор работает
на многих компьютерах. Даже в популярной операционной системе Mac OS X разработчикам доступна команда rcs. RCS сохраняет на диске в специальном формате
набор всех внесенных изменений (то есть разницу между файлами). В будущем
это позволяет воссоздать любой файл в любой момент времени, добавив к нему
все изменения.

Централизованные системы контроля версий
Следующая большая проблема — необходимость сотрудничать с разработчиками
других систем. Для ее решения были разработаны централизованные системы
контроля версий (Centralized Version Control System, CVCS). Они, как и системы
CVS, Subversion и Perforce, обладают единым сервером, содержащим все версии

Управление версиями   21

файлов, и набором клиентов, выгружающих файлы с сервера (рис. 1.2). На протяжении многих лет это было стандартом управления версиями.

Компьютер A

ЦЕНТРАЛЬНЫЙ VCS-СЕРВЕР

Файл

БАЗА ДАННЫХ ВЕРСИЙ

Версия 3

Версия 2
Компьютер B
Файл

Версия 1

Рис. 1.2. Централизованное управление версиями

Такая схема имеет много преимуществ, особенно перед локальными системами
контроля версий. Например, каждый человек, работающий над проектом, до определенной степени знает, чем занимаются его коллеги. Администраторы могут детально контролировать права допуска прочих сотрудников; администрировать CVCS
намного проще, чем управлять локальными базами данных на каждом клиенте.
Однако есть у этой схемы и серьезные недостатки. Самым очевидным является
единая точка отказа, представленная центральным сервером. Отключение этого
сервера на час означает, что в течение часа любые взаимодействия невозможны. То
есть вы не сможете сохранять вносимые изменения. При повреждении жесткого
диска центральной базы данных и отсутствии нужных резервных копий теряется вся
информация — вся история разработки проекта за исключением единичных снимков состояния, которые могут остаться на локальных компьютерах пользователей.
Впрочем, та же самая проблема характерна и для локальных систем контроля версий — храня историю разработки проекта в одном месте, вы рискуете потерять все.

Распределенные системы контроля версий
Именно здесь на первый план выходят распределенные системы контроля версий
(Distributed Version Control System, DVCS). В DVCS (к примеру, Git, Mercurial,
Bazaar или Darcs) клиенты не просто выгружают последние снимки файлов, они
создают полную зеркальную копию репозитория. Соответственно в случае выхода

22    Глава 1 • Начало работы
из строя одного из серверов его работоспособность можно восстановить, скопировав
один из клиентских репозиториев (рис. 1.3). Каждая такая выгрузка сопровождается
полным резервным копированием всех данных.
СЕРВЕР
БАЗА ДАННЫХ ВЕРСИЙ
Версия 3

Версия 2

Версия 1

КОМПЬЮТЕР A

КОМПЬЮТЕР B

Файл

Файл

БАЗА ДАННЫХ ВЕРСИЙ

БАЗА ДАННЫХ ВЕРСИЙ

Версия 3

Версия 3

Версия 2

Версия 2

Версия 1

Версия 1

Рис. 1.3. Распределенное управление версиями

Более того, многие из этих систем обладают несколькими удаленными репозиториями, что позволяет разным рабочим группам сотрудничать в рамках одного
проекта. Допустима настройка разных типов рабочих процессов, невозможная
в централизованных системах, например в иерархических моделях.

Основы Git   23

Краткая история Git
Подобно множеству других замечательных программных продуктов, система Git
начиналась с небольшого созидательного разрушения и пылкой полемики.
Ядро Linux представляет собой крайне масштабный проект ПО с открытым исходным кодом. В истории поддержки ядра Linux изменения программ долгое время
передавались в виде исправлений (patches) и архивированных файлов. В 2002 году
для проекта Linux стали использовать собственную систему DVCS, которая называлась BitKeeper.
В 2005 году отношения между сообществом, разрабатывавшим ядро Linux, и коммерческой фирмой, создавшей BitKeeper, были разорваны и бесплатное использование этой системы контроля версий стало невозможным, что побудило сообщество
разработчиков Linux (и в частности создателя этой операционной системы Линуса
Торвальдса) начать работу над собственным инструментом, взяв за основу некоторые
идеи BitKeeper. Вот цели, которые ставились для новой системы:
‰‰ быстродействие;
‰‰ простое проектное решение;
‰‰ мощная поддержка нелинейной разработки (тысячи параллельных ветвей);
‰‰ полностью распределенная система;
‰‰ возможность эффективной (в плане быстродействия и объема данных) работы

с большими проектами, такими как ядро Linux.

С момента своего появления в 2005 году система Git развивалась и совершенствовалась в попытках добиться простоты использования при сохранении изначальных
характеристик. Она работает необыкновенно быстро, крайне эффективна для
больших проектов и обладает потрясающей ветвящейся системой нелинейной
разработки (см. главу 3).

Основы Git
Так что же в общих чертах представляет собой Git? Крайне важно усвоить информацию этого раздела, так как если вы поймете, что такое система Git и как
выглядят основные принципы ее работы, вам будет намного проще эффективно
ее использовать. В ходе знакомства с Git постарайтесь забыть обо всем, что вы
можете знать о других VCS, таких как Subversion и Perforce; это позволит при
работе с данным инструментом избежать путаницы в нюансах. Система Git
хранит и воспринимает информацию не так, как они, несмотря на сходный
пользовательский интерфейс. Именно понимание этих различий позволит вам
избежать ошибок.

24    Глава 1 • Начало работы

Снимки состояний, а не изменений
Главным отличием Git от любой другой системы контроля версий (в том числе
Subversion и ей подобных) является восприятие данных. Большинство систем
хранит информацию в виде списка изменений, связанных с файлами. Эти системы
(CVS, Subversion, Perforce, Bazaar и т. п.) рассматривают хранимые данные как
набор файлов и изменений, которые вносились в эти файлы в течение их жизни
(рис. 1.4).
История контрольных записей

Версия 1

Версия 2

Файл A

∆1

Версия 3

∆1

∆1

Версия 5

∆2

Файл B

Файл C

Версия 4

∆2

∆2

∆3

Рис. 1.4. Хранение данных в виде изменений, вносимых в базовую версию
каждого файла

Система Git не воспринимает и не хранит файлы подобным образом. В ее восприятии данные представляют собой набор снимков состояния миниатюрной файловой
системы. Каждый раз, когда вы создаете новую версию или сохраняете состояние
проекта в Git, по сути, делается снимок всех файлов в конкретный момент времени и сохраняется ссылка на этот снимок. Для повышения продуктивности вместо
файлов, которые не претерпели изменений, сохраняется всего лишь ссылка на их
ранее сохраненные версии (рис. 1.5). Git воспринимает данные скорее как поток
снимков состояния (stream of snapshots).
Это важное отличие Git почти от всех остальных VCS. И именно из-за него в Git
приходится пересматривать практически все аспекты управления версиями,
которые остальные системы скопировали у своих предшественниц. В результате
Git больше напоминает не простую систему контроля версий, а миниатюрную
файловую систему с удивительно мощным инструментарием. Выгоды, которые
дает подобное представление данных, мы обсудим в главе 3, когда будем рассматривать ветвления.

Основы Git   25
История контрольных записей

Версия 1

Версия 2

Версия 3

Версия 4

Версия 5

Файл A

A1

A1

A2

A2

Файл B

B

B

B1

B2

Файл C

C1

C2

C2

C3

Рис. 1.5. Хранение данных в виде снимков состояния проекта

Локальностьопераций
Для осуществления практически всех операций системе Git требуются только
локальные файлы и ресурсы — в общем случае информация с других компьютеров
сети не нужна. Если вы привыкли к CVCS, где для большинства операций характерны задержки из-за передачи данных по сети, этот аспект работы Git наводит
на мысль, что боги скорости наделили эту систему нереальной силой. Когда вся
история проекта хранится на локальном диске, кажется, что большинство операций
выполняется почти мгновенно.
Например, для просмотра истории проекта системе Git нет нужды обращаться
к серверу, получать там историю и выводить ее на экран — система просто читает все непосредственно из локальной базы данных. То есть вы видите историю
проекта практически сразу же. Если вы хотите посмотреть, чем текущая версия
файла отличается от версии месячной давности, Git ищет старый файл и вычисляет
внесенные в него правки, вместо того чтобы просить об этой операции удаленный сервер или считывать с этого сервера старую версию файла для локального
сравнения.
Это также означает, что практически все операции могут проводиться в автономном
режиме и без использования виртуальной частной сети (Virtual Private Network,
VPN). Если желание поработать появится в самолете или в поезде, вы можете
успешно фиксировать изменения, пока не найдете подключение к сети для их выгрузки. Если дома вы не можете добиться корректной работы VPN-клиента, вам
это не помешает. Во многих других системах подобное либо невозможно, либо
достигается сложным путем и требует больших усилий. Например, в Perforce при

26    Глава 1 • Начало работы
отсутствии подключения к серверу многие действия недоступны; а в Subversion
и в CVS можно редактировать файлы, но нельзя фиксировать внесенные изменения
в базе данных (так как она недоступна). Может показаться, что это мелочи, но вы
удивитесь, обнаружив, насколько большое значение они имеют.

Целостность Git
В системе Git для всех данных перед сохранением вычисляется контрольная сумма,
по которой они впоследствии ищутся. То есть сохранить содержимое файла или
папки таким образом, чтобы система Git об этом не узнала, невозможно. Эта функциональность встроена в Git на самом низком уровне и является неотъемлемым
принципом ее работы. Невозможно потерять информацию или повредить файл
скрытно от Git.
Механизм, которым пользуется Git для вычисления контрольных сумм, называется
хешем SHA-1. Это строка из 40 символов, включающая в себя числа в шестнадцатеричной системе (0–9 и a–f) и вычисляемая на основе содержимого файла или
структуры папки в Git. Хеш SHA-1 выглядит примерно так:
24b9da6552252987aa493b52f8696cd6d3b00373

Вы будете постоянно наталкиваться на эти хеш-значения, так как Git использует
их повсеместно. По сути, Git сохраняет данные в базе не по именам файлов, а по
хешу их содержимого.

Git, как правило, только добавляет данные
Практически все операции в Git приводят к добавлению данных в базу. Систему
сложно заставить выполнить неотменяемое действие или каким-то образом стереть
данные. Как и при работе с любой VCS, незафиксированные данные можно потерять или повредить; но как только снимок состояния зафиксирован, потерять его
становится крайне сложно, особенно если вы регулярно копируете базу в другой
репозиторий.
Это делает работу с Git крайне комфортной, так как можно экспериментировать,
не опасаясь серьезно навредить проекту.

Три состояния
А сейчас внимание! Это основное, что требуется запомнить, чтобы дальнейшее
освоение Git прошло без проблем. Файлы в Git могут находиться в трех основных состояниях: зафиксированном, модифицированном и индексированном.

Основы Git   27

Зафиксированное (committed) состояние означает, что данные надежно сохранены
в локальной базе. Модифицированное (modified) состояние означает, что изменения
уже внесены в файл, но пока не зафиксированы в базе данных. Индексированное
(staged) состояние означает, что вы пометили текущую версию модифицированного
файла как предназначенную для следующей фиксации.
В результате Git-проект разбивается на три основные области: папка Git, рабочая
папка и область индексирования (рис. 1.6).

Рабочая папка

Область индексирования

Папка .git (репозиторий)

Контрольное считывание проекта

Запись индексированных
состояний
Фиксация

Рис. 1.6. Рабочая папка, область индексирования и папка Git

Папка Git — это место, где Git хранит метаданные и объектную базу данных проекта.
Это наиболее важная часть Git, которая копируется при дублировании репозитория
(хранилища) с другого компьютера.
Рабочая папка — это место, куда выполняется выгрузка одной из версий про­
екта. Эти файлы извлекаются из сжатой базы данных в папке Git и помещаются
на жесткий диск вашего компьютера, готовые к использованию или редакти­
рованию.
Область индексирования — это файл, обычно находящийся в папке Git и хранящий
информацию о том, что именно войдет в следующую операцию фиксации. Иногда
ее еще называют промежуточной областью.
Базовый рабочий процесс в Git выглядит так:
1. Вы редактируете файлы в рабочей папке.
2. Вы индексируете файлы, добавляя их снимки в область индексирования.
3. Вы выполняете фиксацию, беря файлы из области индексирования и сохраняя
снимки в папке Git.

28    Глава 1 • Начало работы
При наличии конкретной версии файла в папке Git файл считается зафиксированным. Если после внесения изменений в файл он был перемещен в область
индексирования, он называется проиндексированным. А отредактированный
после выгрузки, но не проиндексированный файл называется модифицированным. В главе 2 мы подробно рассмотрим все эти состояния. Заодно вы научитесь
пользоваться преимуществами, которые они предоставляют, и пропускать этап
индексирования.

Командная строка
Использовать Git можно разными способами. Для этого существуют как инструменты командной строки, так и многочисленные графические пользовательские
интерфейсы с различными возможностями. В этой книге рассматривается только
работа с Git из командной строки. Главным образом потому, что это единственный
способ выполнения всех Git-команд, — в большинстве GUI для простоты реализованы только некоторые варианты функциональности Git. Кроме того, человек,
умеющий работать с Git из командной строки, скорее всего, сможет разобраться, что
делать с GUI-версией, а вот обратное неверно. И если выбор графического клиента
зависит от личных предпочтений, то инструменты командной строки доступны
всем без исключения пользователям.
Предполагается, что вы знаете, как открыть терминал в Mac и приглашение на
ввод командной строки или оболочку Powershell в Windows. Если вы не понимаете,
о чем идет речь, нужно познакомиться с данными инструментами, чтобы следовать
приводимым в книге примерам и описаниям.

Установка Git
Перед тем как приступить к работе с Git, эту систему следует установить на компьютер. Если она у вас уже установлена, имеет смысл обновить ее до последней
версии. Вы можете воспользоваться менеджером пакетов или другим средством
установки или загрузить исходный код и скомпилировать его самостоятельно.
ПРИМЕЧАНИЕ
При написании этой книги использовалась система Git версии 2.0.0. Хотя большинство
демонстрируемых команд должно работать даже в самых старых версиях Git, некоторые
из них у пользователей устаревших систем могут не работать или работать не так, как
описывается. Но так как в Git отлично реализован принцип обратной совместимости,
любая версия выше, чем 2.0, должна работать требуемым образом.

Установка Git   29

Установка в Linux
Если вы хотите установить Git в операционной системе Linux при помощи установщика бинарных пакетов, в общем случае можно воспользоваться инструментом
управления пакетами, входящим в имеющийся у вас дистрибутив. Например, в дистрибутиве Fedora применяется утилита yum:
$ yum install git

Если вы пользуетесь дистрибутивом семейства Debian, например Ubuntu, попробуйте утилиту apt-get:
$ apt-get install git

Инструкции по установке в других операционных системах семейства Unix вы
найдете на сайте Git (http://git-scm.com/download/linux ).

Установка в Mac
Существует несколько способов установки Git на компьютерах Mac. Проще всего,
наверное, установить инструменты командной строки Xcode. В операционной системе Mavericks (10.9) и выше достаточно просто попытаться запустить git через
терминал. Если система еще не установлена, вам будет предложено ее установить.
Те, кому требуется самая последняя версия, могут воспользоваться установщиком
бинарных пакетов. Установщик Git для OSX доступен на сайте http://git-scm.com/
download/mac (рис. 1.7).

Рис. 1.7. Установщик Git OSX

30    Глава 1 • Начало работы
Еще можно установить Git как часть сервиса GitHub для Mac. Инструмент GUI
Git дает возможность добавлять инструменты командной строки. Загрузить его
можно с сайта http://mac.github.com.

Установка в Windows
Установка Git в операционной системе Windows также осуществляется разными
способами. Официальный вариант системы доступен на сайте Git. Достаточно
зайти­на страницу http://git-scm.com/download/win, и загрузка начнется автоматически. Обратите внимание, что этот проект называется Git для Windows (или
msysGit) и отличается от Git; дополнительная информация находится на сайте
http://msysgit.github.io/.
Другой простой способ — это установка GitHub для Windows. Пакет установки
включает в себя версию как с командной строкой, так и с GUI. Он хорошо работает
с оболочкой Powershell и обеспечивает надежное кэширование учетных данных и
работоспособные настройки CRLF. Подробно мы рассмотрим эти вещи чуть позже,
пока же достаточно сказать, что они вам потребуются. Загрузить эту версию можно
с сайта http://windows.github.com.
Некоторые пользователи предпочитают установку Git из исходного кода, потому
что это позволяет получить самую новую версию. Немного отстают установщики
бинарных пакетов, хотя в последние годы развитие Git понемногу стирает эту
разницу.
Для установки Git из исходного кода вам потребуются следующие библиотеки, от
которых зависит эта система: curl, zlib, openssl, expat и libiconv. В системах с
инструментом yum (таких как Fedora) или apt-get (как системы семейства Debian)
для установки всех зависимостей можно воспользоваться следующими командами:
$ yum install curl-devel expat-devel gettext-devel \
openssl-devel zlib-devel
$ apt-get install libcurl4-gnutls-dev libexpat1-dev gettext \
libz-dev libssl-dev

При наличии всех нужных зависимостей архив tar с последней версией можно
найти в нескольких местах. Например, загрузить с сайта Kernel.org (https://www.
kernel.org/pub/software/scm/git) или с зеркала сайта GitHub (https://github.com/git/git/
releases). Как правило, последнюю версию проще найти на странице GitHub, но
и на сайте kernel.org есть подписи версий, позволяющие проверить, что именно
вы загружаете.
Затем остаются процедуры компиляции и установки:
$
$
$
$

tar -zxf git-1.9.1.tar.gz
cd git-1.9.1
make configure
./configure --prefix=/usr

Первая настройка Git   31
$ make all doc info
$ sudo make install install-doc install-html install-info

После этого обновления можно загружать через саму систему Git:
$ git clone git://git.kernel.org/pub/scm/git/git.git

Первая настройка Git
Теперь, когда на вашей машине установлена система Git, нужно настроить ее
окружение. На каждом компьютере эта процедура проводится только один раз;
при обновлениях все настройки сохраняются. Впрочем, вы можете изменить их
в любой момент, снова воспользовавшись указанными командами.
Система Git поставляется с инструментом git config, позволяющим получать
и устанавливать переменные конфигурации, которые задают все аспекты внешнего
вида и работы Git. Эти переменные хранятся в разных местах.
‰‰ Файл /etc/gitconfig содержит значения, действующие для всех пользователей

системы и всех их репозиториев. Указав параметр --system при запуске git
config, вы добьетесь чтения и записи для этого конкретного файла.

‰‰ Файл ~/.gitconfig или ~/.config/git/config связан с конкретным пользо-

вателем. Чтение и запись для этого файла инициируются передачей параметра
--global.

‰‰ Параметры конфигурационного файла в папке Git (то есть .git/config )

репозитория, с которым вы работаете в данный момент, действуют только на
конкретный репозиторий.

Настройки каждого следующего уровня переопределяют настройки предыдущего,
то есть конфигурация из .git/config перекроет конфигурацию из /etc/gitconfig.
В системах семейства Windows Git ищет файл .gitconfig в папке $HOME (в большинстве случаев она вложена в папку C:\Users\$USER). Кроме того, ищется файл
/etc/gitconfig, но уже относительно корневого каталога MSys, расположение
которого вы сами выбираете при установке Git.

Ваш идентификатор
При установке Git первым делом следует указать имя пользователя и адрес
электронной почты. Это важно, так как данную информацию Git будет включать
в каждую фиксируемую вами версию, и она обязательно включается во все создаваемые вами коммиты (зафиксированные данные):
$ git config --global user.name "John Doe"
$ git config --global user.email johndoe@example.com

32    Глава 1 • Начало работы
Передача параметра --global позволяет сделать эти настройки всего один раз,
так как в этом случае Git будет использовать данную информацию для всех ваших
действий в системе. Если для конкретного проекта требуется указать другое имя
или адрес электронной почты, войдите в папку с проектом и выполните эту команду
без параметра --global.
Многие GUI-инструменты помогают выполнять эти действия при своем первом запуске.

Выбор редактора
Итак, ваши личные данные указаны, теперь пришло время выбрать текстовый
редактор, который будет по умолчанию использоваться, когда вам требуется напечатать сообщение в Git. Без этой настройки Git задействует стандартный редактор
операционной системы — обычно это Vim. Если вы предпочитаете другой текстовый
редактор, например Emacs, сделайте следующее:
$ git config --global core.editor emacs

ПРИМЕЧАНИЕ
Программы Vim и Emacs представляют собой популярные текстовые редакторы, часто
используемые разработчиками операционных систем семейства Unix, таких как Linux
и Mac. Если вы никогда ими не пользовались или работаете в операционной системе
Windows, вам может потребоваться руководство по настройке вашего любимого текстового редактора в Git.

Проверка настроек
Проверить выбранные настройки позволяет команда git config --list, выводящая
список всех обнаруженных в текущий момент параметров:
$ git config --list
user.name=John Doe
user.email=johndoe@example.com
color.status=auto
color.branch=auto
color.interactive=auto
color.diff=auto
...

Некоторые ключи могут появляться более одного раза, так как Git считывает один
и тот же ключ из разных файлов (например, /etc/gitconfig и ~/.gitconfig).
В этом случае система использует последнее значение для каждого обнаруженного
ею уникального ключа.
Кроме того, можно проверить значение конкретного ключа, воспользовавшись
командой git config :
$ git config user.name
John Doe

Заключение   33

Получение справочной информации
Существует три способа доступа к странице со справочной информацией по любой
Git-команде:
$ git help
$ git --help
$ man git-

К примеру, справка по команде config открывается так:
$ git help config

Прелесть этих команд состоит в том, что пользоваться ими можно даже без подключения к сети. Если справочной информации и этой книги недостаточно и вам
требуется персональная помощь, воспользуйтесь каналами #git или #github
IRC-сервера Freenode (irc.freenode.net). Обычно там общаются сотни людей, хорошо
разбирающихся в Git и готовых прийти на помощь.

Заключение
К этому моменту вы должны в общих чертах представлять, что такое система Git
и чем она отличается от централизованных систем управления версиями, которыми
вы могли пользоваться ранее. Кроме того, у вас должна быть установлена рабочая
версия Git с вашими личными настройками. Теперь все готово для изучения основ
Git.

2

Основы Git

Если ваша цель — приступить к работе с Git, прочитав всего одну главу, то эта глава
перед вами. Именно здесь вы найдете описания базовых команд, необходимых для
решения подавляющего большинства возникающих в процессе использования Git
задач. Прочитав эту главу, вы научитесь настраивать и инициализировать репозитории, начинать и прекращать слежение за файлами, индексировать и фиксировать
изменения. Заодно мы покажем вам, как заставить Git игнорировать отдельные
файлы и их группы, как быстро и просто отменить ошибочные изменения, как
посмотреть историю проекта и изменения, вносившиеся в каждую версию, как отправить файл в удаленный репозиторий и извлечь его оттуда.

Создание репозитория в Git
Есть два подхода к созданию Git-проекта. Можно взять существующий проект или
папку и импортировать в Git. А можно клонировать уже существующий репозиторий с другого сервера.

Инициализация репозитория в существующей папке
Чтобы начать слежение за существующим проектом, перейдите в папку этого проекта и введите команду
$ git init

Создание репозитория в Git   35

В результате в существующей папке появится еще одна папка с именем .git и всеми
нужными вам файлами репозитория — это будет основа вашего Git-репозитория.
Пока контроль ваших файлов отсутствует. (Подробное описание файлов, входящих
в папку .git, вы найдете в главе 10.)
Чтобы начать управление версиями существующих файлов (в противовес пустому каталогу), укажите файлы, за которыми должна следить система, и выполни­те
первую фиксацию изменений. Для этого потребуется несколько команд git add,
добавляющих файлы, за которыми вы хотите следить, а затем команда git commit:
$ git add *.c
$ git add LICENSE
$ git commit -m 'первоначальная версия проекта'

Чуть позже мы подробно рассмотрим, что делают эти команды. А пока у вас ­есть репозиторий Git с добавленными туда файлами и первой зафиксированной версией.

Клонирование существующего репозитория
Получение копии существующего репозитория, например проекта, в котором вы
хотите принять участие, выполняется командой git clone. Те, кто знаком с другими VCS, например с Subversion, могли заметить, что в данном случае использу­
ется команда клонирования (clone), а не выгрузки (checkout). Это крайне важное
отличие. Вы получаете не просто рабочую копию, а полную копию практически всех
данных с сервера. Команда git clone по умолчанию забирает все версии всех файлов
за всю историю проекта. Это означает, что при повреждении серверного диска практически любой клон на любом из клиентов может использоваться для возвращения
сервера в состояние, в котором он пребывал до момента клонирования (при этом
может быть утрачена часть хуков со стороны сервера, но все данные, относящиеся
к версиям, будут на месте). Подробно эта тема рассматривается в главе 4.
Клонирование репозитория осуществляется командой git clone [url]. К примеру,
вот как клонируется подключаемая Git-библиотека libgit2:
$ git clone https://github.com/libgit2/libgit2

Команда создает папку с именем libgit2, инициализирует в ней папку .git, считывает из репозитория все данные и выгружает рабочую копию последней версии.
В папке libgit2 вы найдете все файлы проекта, подготовленные к работе. Репозиторий можно клонировать и в другое место, достаточно указать имя нужной папки
в следующем параметре командной строки:
$ git clone https://github.com/libgit2/libgit2 mylibgit

Эта команда делает то же самое, что и предыдущая, но все файлы оказываются
в папке mylibgit.
Для работы Git применяются разные транспортные протоколы. В предыдущем
примере использовался протокол https://, но можно также встретить вариант

36    Глава 2 • Основы Git
git:// или user@server:path/to/repo.git. В последнем случае применяется про-

токол SSH. Все доступные варианты серверных конфигураций, обеспечивающих
доступ к Git-репозиторию, со всеми их достоинствами и недостатками, рассматриваются в главе 4.

Запись изменений в репозиторий
Итак, у вас есть настоящий Git-репозиторий и некая выгрузка, то есть рабочие копии
файлов нашего проекта. Теперь в файлы можно вносить изменения и фиксировать
их, как только проект достигнет состояния, которое вы хотели бы сохранить.
Помните, что каждый файл в рабочей папке может пребывать в одном из двух состояний: отслеживаемом и неотслеживаемом. Первый случай — это файлы, входящие в последний снимок системы; они могут быть неизмененными, измененными и
подготовленными к фиксации. Второй случай — это все остальные файлы рабочей
папки, не вошедшие в последний снимок системы и не проиндексированные для
последующей фиксации. После первого клонирования репозитория все файлы
оказываются отслеживаемыми и неизмененными, потому что вы просто выгрузили
их и пока не отредактировали.
Отредактированный файл Git рассматривает как измененный, ведь его состояние
отличается от последнего зафиксированного. Вы индексируете эти измененные
файлы, фиксируете все проиндексированные изменения, после чего цикл повторяется (рис. 2.1).
Неотслеживаемый

Неизмененный

Измененный

Проиндексированный

Добавляем файл

Редактируем
файл
Удаляем файл

Индексируем
файл

Фиксация

Рис. 2.1. Жизненный цикл состояния файлов

Запись изменений в репозиторий   37

Проверка состояния файлов
Основным инструментом определения состояния файлов является команда
git status. Непосредственно после клонирования она дает примерно такой результат:
$ git status
On branch master
nothing to commit, working directory clean

Это означает, что в рабочей папке отсутствуют отслеживаемые и измененные файлы. Кроме того, система Git не обнаружила неотслеживаемых файлов, в противном
случае они были бы перечислены в выводимых командой данных. Наконец, команда
сообщает имя ветки, на которой вы в данный момент находитесь, и информирует
о совпадении состояний этой ветки и аналогичной ветки на сервере. Пока мы
будем работать только с веткой master, предлагаемой по умолчанию, тем более
что на данном этапе эта информация не имеет особого значения. Ветки и ссылки
подробно обсуждаются в главе 3.
Предположим, вы добавили в проект простой файл README. Если до этого момента
он не существовал, команда git status покажет данный неотслеживаемый файл
примерно так:
$ echo 'My Project' > README
$ git status
On branch master
Untracked files:
(use "git add ..." to include in what will be committed)
README
nothing added to commit but untracked files present (use "git add" to track)

Понять, что новый файл README является неотслеживаемым, можно по заголовку
Untracked files в выводимых командой status данных. Указанное состояние,
по сути, означает, что Git видит файл, отсутствующий в предыдущем снимке состояния (в коммите); без явного указания с вашей стороны Git не будет добавлять
этот файл в число коммитов. Подобный подход гарантирует, что в репозитории не
сможет случайно появиться сгенерированный бинарный файл или другие файлы,
которые вы не собирались туда добавлять. Однако файл README туда добавить
нужно, поэтому давайте сделаем его отслеживаемым.

Слежение за новыми файлами
Чтобы начать слежение за новым файлом, воспользуйтесь командой git add. К примеру, для файла README она будет выглядеть так:
$ git add README

38    Глава 2 • Основы Git
Теперь команда status покажет, что этот файл является отслеживаемым и проиндексированным:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: README

Все проиндексированные файлы перечисляются под заголовком Changes to be
committed. Если в этот момент произвести фиксацию, версия файла, существовавшая на момент выполнения команды git add, попадет в историю снимков состояния.
Надеюсь, вы помните, что за командой git init следовала команда git add (имена
файлов), что заставило систему начать слежение за файлами в папке. Команда git
add работает с маршрутом доступа к файлу или к папке; если указан путь к папке,
команда рекурсивно добавляет все находящиеся в ней файлы.

Индексация измененных файлов
Давайте внесем изменения в файл, находящийся под наблюдением. Если отредактировать ранее отслеживаемый файл benchmarks.rb и воспользоваться командой
git status, результат будет примерно таким:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: README
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Файл benchmarks.rb появляется под заголовком Changed but not staged for
commit — это означает, что отслеживаемый файл из рабочей папки был изменен,
но пока не проиндексирован. Индексирование выполняется уже знакомой вам
командой git add. Это многоцелевая команда, позволяющая начать слежение за
новыми файлами, произвести индексирование файлов, а также пометить файлы с
конфликтом слияния как разрешенные. Возможно, целесообразнее воспринимать
эту команду как «добавление содержимого к следующему коммиту», а не как «добавление файла к проекту». Итак, воспользуемся командой git add для индексирования файла benchmarks.rb, а затем запустим команду git status:
$ git add benchmarks.rb
$ git status
On branch master

Запись изменений в репозиторий   39
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: README
modified: benchmarks.rb

Теперь оба файла проиндексированы и войдут в следующий коммит. Но предположим, что вы вспомнили про небольшое изменение, которое следует внести в файл
benchmarks.rb до его фиксации. Вы снова открываете файл, редактируете его, после
чего все готово к фиксации. Но сначала еще раз воспользуемся командой git status:
$ vim benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: README
modified: benchmarks.rb
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Удивительное дело. Теперь утверждается, что файл benchmarks.rb одновременно
является и проиндексированным, и непроиндексированным. Как такое может
быть? Оказывается, Git индексирует файл в том состоянии, в котором он пребывал
на момент выполнения команды git add. Если сейчас зафиксировать изменения,
в коммит войдет версия файла benchmarks.rb, появившаяся после последнего
запуска команды git add, а не версия, находившаяся в рабочей папке при запуске
команды git commit. Редактирование файла после выполнения команды git add
требует повторного запуска этой команды для индексирования самой последней
версии файла:
$ git add benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
new file: README
modified: benchmarks.rb

Краткий отчет о состоянии
Команда git status дает исчерпывающий, хотя и многословный результат. Но в Git
существует флаг, позволяющий получить сведения в более компактной форме. Запустив команду git status -s или git status --short, вы получите упрощенный
вариант вывода.

40    Глава 2 • Основы Git
$ git status -s
M README
MM Rakefile
A lib/git.rb
M lib/simplegit.rb
?? LICENSE.txt

Рядом с именами новых неотслеживаемых файлов стоит знак ??, новые файлы,
добавленные в область предварительной подготовки, помечены буквой A, а модифицированные файлы — буквой M и т. п. Пометки выводятся в два столбца: в первом
указывается, индексирован ли файл, а во втором — вносились ли в него изменения.
Скажем, в приведенном примере файл README модифицирован в рабочей папке, но
пока не проиндексирован, в то время как файл lib/simplegit.rb модифицирован
и проиндексирован одновременно. Файл Rakefile был модифицирован, проиндексирован и снова модифицирован, то есть в нем присутствуют как индексированные,
так и неиндексированные данные.

Игнорирование файлов
Бывает так, что некоторый класс файлов вы не хотите ни автоматически добавлять в репозиторий, ни видеть в списке неотслеживаемых. В эту категорию,
как правило, попадают автоматически генерируемые файлы, например журналы
­регистрации или файлы, генерируемые системой сборки. В подобных случаях
создается файл .gitignore со списком соответствующих паттернов. Вот пример
такого файла:
$ cat .gitignore
*.[oa]
*~

Первая строка заставляет Git игнорировать любые файлы, заканчивающиеся на «.o»
или «.a», — объектные и архивные файлы, которые могут появляться в процессе
сборки кода. Вторая строка предписывает игнорировать файлы, заканчивающиеся
на тильду (~). Этим значком многие текстовые редакторы, в том числе Emacs, обозначают временные файлы. В этот список можно включить также папки log, tmp
или pid, автоматически генерируемую документацию и т. п. Работу всегда имеет
смысл начинать с настройки файла .gitignore, так как это защищает от случайного
добавления в репозиторий файлов, которые там не нужны.
Вот правила для паттернов, которые можно вставлять в файл .gitignore.
‰‰ Игнорируются пустые строки и строки, начинающиеся с символа #.
‰‰ Работают стандартные глобальные паттерны.
‰‰ Паттерны можно заканчивать слешем (/), указывая папку.
‰‰ Можно инвертировать паттерн, начав его с восклицательного знака (!).

Запись изменений в репозиторий   41

Глобальные паттерны напоминают упрощенные регулярные выражения, используемые интерпретаторами команд. Звездочка (*) замещает произвольное число
символов, запись [abc] соответствует любому символу из указанных в скобках
(в данном случае это символ a, b или c), знак вопроса (?) замещает собой один
символ, а разделенные дефисом символы в квадратных скобках ([0-9]) совпадают
с любым символом из указанного диапазона (в данном случае от 0 до 9). Кроме
того, две звездочки применяются для обозначения вложенных папок; запись a/**/z
может означать a/z, a/b/z, a/b/c/z и т. д.
Вот еще один пример файла .gitignore:
# комментарий – это игнорируется
*.a
# пропускать файлы, заканчивающиеся на .a
!lib.a # но отслеживать файлы lib.a, несмотря на пропуск файлов на .a
/TODO # игнорировать только корневой файл TODO, а не файлы вида subdir/TODO
build/
# игнорировать все файлы в папке build/
doc/*.txt # игнорировать doc/notes.txt, но не doc/server/arch.txt

СОВЕТ
На сайте GitHub вы найдете исчерпывающий список хороших примеров файла .gitignore
для десятков проектов и языков. Он находится на странице https://github.com/github/
gitignore. Им можно воспользоваться как отправной точкой для ваших собственных
проектов.

Просмотр индексированных и неиндексированных
изменений
Если команда git status дает недостаточно подробный, с вашей точки зрения,
­результат, например если вы хотите не только получить список отредактированных
файлов, но и узнать, что именно изменилось, воспользуйтесь командой git diff.
Подробно она рассматривается чуть позже, но чаще всего вы будете пользоваться
ею для ответа на два вопроса: что вы отредактировали (но пока не проиндексировали) и что из проиндексированного готово к фиксации? Команда git status
отвечает на эти вопросы в общей форме, перечисляя имена файлов, а вот команда
git diff показывает добавленные и удаленные строки — то есть все вставки в
программу.
Предположим, вы еще раз отредактировали и проиндексировали файл README,
а затем подвергли редактированию без последующего индексирования файл
benchmarks.rb. Команда git status в этом случае даст примерно вот такой результат:
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)

42    Глава 2 • Основы Git
new file: README
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Команда git diff без дополнительных параметров позволяет посмотреть, что было
изменено, но пока не проиндексировано:
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end
+
+
+
+

run_code(x, 'commits 1') do
git.commits.size
end
run_code(x, 'commits 2') do
log = git.commits('master', 15)
log.size

Эта команда сравнивает содержимое в рабочей папке и в области индексиро­
вания. Она выводит список изменений, которые вы внесли, но пока не проиндексировали.
Чтобы посмотреть, что из проиндексированного войдет в следующий коммит, воспользуйтесь командой git diff --staged. Эта команда сравнивает индексированные
изменения с содержимым последней зафиксированной версии:
$ git diff --staged
diff --git a/README b/README
new file mode 100644
index 0000000..03902a1
--- /dev/null
+++ b/README
@@ -0,0 +1,4 @@
+My Project
+
+ This is my project and it is amazing.
+

Важно помнить, что сама по себе команда git diff показывает не все изменения,
сделанные с момента последней фиксации состояния, а только те, которые еще не
проиндексированы. То есть если вы проиндексировали все сделанные изменения,
команда git diff ничего вам не вернет.

Запись изменений в репозиторий   43

В то же время если вы проиндексировали файл benchmarks.rb, а затем отредактировали его, команда git diff покажет как проиндексированные, так и непроиндексированные изменения в этом файле:
$ git add benchmarks.rb
$ echo '# test line' >> benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: benchmarks.rb
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Теперь можно воспользоваться командой git diff и узнать, что осталось без индексации:
$ git diff
diff --git a/benchmarks.rb b/benchmarks.rb
index e445e28..86b2f7c 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -127,3 +127,4 @@ end
main()
##pp Grit::GitRuby.cache_client.stats
+# test line

А команда git diff --cached покажет проиндексированные изменения:
$ git diff --cached
diff --git a/benchmarks.rb b/benchmarks.rb
index 3cb747f..e445e28 100644
--- a/benchmarks.rb
+++ b/benchmarks.rb
@@ -36,6 +36,10 @@ def main
@commit.parents[0].parents[0].parents[0]
end
+
run_code(x, 'commits 1') do
+
git.commits.size
+
end
+
run_code(x, 'commits 2') do
log = git.commits('master', 15)
log.size

Фиксация изменений
Теперь, когда область индексирования настроена нужным вам образом, можно
зафиксировать внесенные туда изменения. Помните, что все, оставленное неиндексированным, в том числе любые созданные или измененные файлы, ­для которых

44    Глава 2 • Основы Git
после редактирования не была выполнена команда git add, в текущий коммит не
войдет. Все эти файлы останутся на вашем диске как измененные. Впрочем, в рассматриваемом сейчас примере мы будем считать, что последний запуск команды
git status показал все файлы как проиндексированные и все готово к фиксации
изменений. Проще всего осуществить фиксацию командой git commit:
$ git commit

Эта команда открывает выбранный вами текстовый редактор. (Редактор устанавливается переменной окружения оболочки $EDITOR — обычно это vim или emacs,
хотя вы можете выбрать и другой вариант, воспользовавшись упомянутой в главе 1
командой git config --global core.editor.)
В редакторе вы увидите следующий текст (в данном случае для примера взят редактор Vim):
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
# On branch master
# Changes to be committed:
# new file: README
# modified: benchmarks.rb
#
~
~
~
«.git/COMMIT_EDITMSG» 9L, 283C

Как видите, по умолчанию сообщение фиксации представляет собой превращенный
в комментарий результат работы команды git status с пустой строкой сверху. Этот
комментарий можно удалить, напечатав вместо него свой вариант, или оставить в
качестве напоминания о том, что именно вы фиксируете. Если вам требуется более
подробная информация об изменениях, можно добавить к команде git commit параметр -v. В результате в редакторе появится список изменений, включаемых в новые
фиксируемые данные. При выходе из редактора Git создает коммит с указанным
сообщением (удаляя комментарии и список изменений).
Сообщение фиксации можно задать и в команде commit, поставив перед ним флаг -m,
как в следующем примере:
$ git commit -m "Story 182: Fix benchmarks for speed"
[master 463dc4f] Story 182: Fix benchmarks for speed
2 files changed, 2 insertions(+)
create mode 100644 README

Итак, вы только что создали первую версию изменений! Надеемся, вы обратили
внимание, что версия предоставила вам ряд данных о себе: зафиксированная вами
ветка (master), контрольная сумма SHA-1 версии (463dc4f), количество подвергшихся изменениям файлов и статистика добавленных и удаленных строк.

Запись изменений в репозиторий   45

Напоминаем, что в коммит записывается снимок состояния, указанного в области
индексирования. Все, что вы не проиндексировали, до сих пор фигурирует в измененном состоянии; для добавления этой информации в репозиторий нужно еще
раз провести фиксацию изменений. При каждой фиксации вы записываете снимок
своего проекта, к которому можно будет вернуться в любой момент или использовать его для сравнения с текущим состоянием.

Пропуск области индексирования
Хотя область индексирования помогает получить именно ту версию состояния, которая­вам требуется, иногда она чрезмерно усложняет рабочий процесс.
Впрочем, в Git есть простой способ обойтись без этой области. Достаточно передать
команде git commit параметр -a, и система Git начнет автоматически индексировать
все отслеживаемые файлы перед их фиксацией, позволяя обойтись без команды
git add:
$ git status
On branch master
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb
no changes added to commit (use "git add" and/or "git commit -a")
$ git commit -a -m 'added new benchmarks'
[master 83e38c7] added new benchmarks
1 file changed, 5 insertions(+), 0 deletions(-)

Как видите, в данном случае перед фиксацией новой версии файла benchmarks.rb
запускать команду git add не нужно.

Удаление файлов
Чтобы система Git перестала работать с файлом, его нужно удалить из числа отслеживаемых (точнее, убрать из области индексирования) и зафиксировать данное
изменение. Это делает команда git rm, которая заодно удаляет указанный файл из
рабочей папки, благодаря чему он исчезает из списка неотслеживаемых.
После простого удаления файла из рабочей папки он появляется в разделе Changed
but not updated (измененные, но необновленные, то есть непроиндексированные)
выводимых командой git status данных:
$ rm grit.gemspec
$ git status

46    Глава 2 • Основы Git
On branch master
Changes not staged for commit:
(use "git add/rm ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
deleted: grit.gemspec
no changes added to commit (use "git add" and/or "git commit -a")

Следующий вызов команды git rm индексирует удаление файла:
$ git rm grit.gemspec
rm 'grit.gemspec'
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
deleted: grit.gemspec

При очередной фиксации изменений файл пропадает и перестает отслеживаться.
Если же вы отредактировали файл и уже добавили его в область индексирования,
следует воспользоваться параметром -f, инициирующим принудительное удаление. Такой подход позволяет избежать случайного удаления данных, которые пока
не были записаны в снимок состояния и поэтому не допускают восстановления
средствами Git.
Иногда требуется оставить файл в рабочей папке, удалив его из области индексирования. Другими словами, нужно, чтобы файл остался на своем месте, но система
Git перестала за ним следить. Эта возможность особенно полезна, когда вы забыли
добавить в файл .gitignore, к примеру, огромный журнал регистрации или набор
скомпилированных файлов .a и по ошибке проиндексировали такой файл или
журнал. В этом случае на помощь приходит параметр --cached:
$ git rm --cached README

Команде git rm можно передавать в качестве аргументов файлы, папки и глобальные
паттерны. То есть можно написать:
$ git rm log/\*.log

Обратите внимание на обратный слеш (\) перед символом *. Он необходим потому, что Git добавляет собственные расширения к расширениям имен файлов
­оболочки. В такой форме команда удаляет все файлы с расширением .log из
папки log/.
Или вы можете написать:
$ git rm \*~

Эта команда удаляет все файлы, имена которых заканчиваются символом ~.

Просмотр истории версий   47

Перемещение файлов
В отличие от многих других VCS, Git в явном виде не отслеживает перемещения
файлов. После переименования файла у системы Git не сохраняется никаких
свидетельствующих об этом событии метаданных. Но Git достаточно искусно позволяет отслеживать сделанные перемещения. Подробно этот процесс рассмотрен
чуть позднее.
При этом, как ни странно, в Git есть команда mv. Для переименования файла можно
написать:
$ git mv file_from file_to

И это отлично сработает. Если после этого посмотреть на статус файла, выяснится,
что Git считает его переименованным:
$ git mv README.md README
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
renamed: README.md -> README

При этом происшедшее эквивалентно выполнению следующих команд:
$ mv README.md README
$ git rm README.md
$ git add README

О том, что произошло переименование, Git узнает косвенным образом, поэтому не
имеет значения, пойдете вы указанным путем или воспользуетесь командой mv. Во
втором случае вы ограничитесь одной командой вместо трех — так что это просто
вспомогательная функция. Важнее всего то, что для переименования можно использовать любой удобный вам инструмент, а команду add/rm достаточно выполнить
позднее, перед фиксацией изменений.

Просмотр истории версий
После сохранения нескольких версий файлов или клонирования уже имеющего
содержимое репозитория вы, скорее всего, захотите взглянуть на то, что было сделано ранее. Базовым и самым мощным инструментом в данном случае является
команда git log.
Мы рассмотрим работу с ней на примере простого проекта simplegit. Для его
получения воспользуйтесь командой:
git clone https://github.com/schacon/simplegit-progit

48    Глава 2 • Основы Git
Примененная к этому проекту команда git log даст следующий результат:
$ git log
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the version number
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon
Date: Sat Mar 15 16:40:33 2008 -0700
removed unnecessary test
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon
Date: Sat Mar 15 10:31:28 2008 -0700
first commit

По умолчанию при отсутствии параметров команда git log выводит в обратном
хронологическом порядке список сохраненных в данный репозиторий версий. То
есть первыми показываются самые свежие коммиты. Как видите, рядом с каждым
коммитом указывается его контрольная сумма SHA-1, имя и электронная почта
автора, дата создания и сообщение о фиксации.
У команды git log существует великое множество параметров, позволяющих
вывести именно ту информацию, которая вам требуется. Рассмотрим несколько
самых популярных.
Одним из самых полезных является параметр -p, показывающий разницу, внесенную каждым коммитом. А дополнительный параметр -2 ограничивает выводимый
результат последними двумя записями:
$ git log -p -2
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number
diff --git a/Rakefile b/Rakefile
index a874b73..8f94139 100644
--- a/Rakefile
+++ b/Rakefile
@@ -5,7 +5,7 @@ require 'rake/gempackagetask'
spec = Gem::Specification.new do |s|
s.platform = Gem::Platform::RUBY
s.name = "simplegit"
s.version = "0.1.0"
+
s.version = "0.1.1"
s.author = "Scott Chacon"
s.email = «schacon@gee-mail.com»

Просмотр истории версий   49
s.summary = "A simple gem for using Git in Ruby code."
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon
Date: Sat Mar 15 16:40:33 2008 -0700
removed unnecessary test
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index a0a60ae..47c6340 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -18,8 +18,3 @@ class SimpleGit
end
end
-if $0 == __FILE__
git = SimpleGit.new
puts git.show
-end
\ No newline at end of file

Данный параметр позволяет вывести на экран ту же информацию, но с добавлением
внесенных изменений, отображаемых непосредственно после каждого коммита. Это
удобно, когда требуется сделать обзор кода или быстро посмотреть, что происходит
после серии коммитов, добавленных коллегой. Еще с командой git log могут использоваться группы суммирующих параметров. К примеру, для получения краткой
статистики по каждой версии применяется параметр --stat:
$ git log --stat
commitca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number
Rakefile | 2 +1 file changed, 1 insertion(+), 1 deletion(-)
commit 085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7
Author: Scott Chacon
Date: Sat Mar 15 16:40:33 2008 -0700
removed unnecessary test
lib/simplegit.rb | 5 ----1 file changed, 5 deletions(-)
commit a11bef06a3f659402fe7563abf99ad00de2209e6
Author: Scott Chacon
Date: Sat Mar 15 10:31:28 2008 -0700
first commit

50    Глава 2 • Основы Git
README
Rakefile
lib/simplegit.rb
3 files changed,

| 6 ++++++
| 23 +++++++++++++++++++++++
| 25 +++++++++++++++++++++++++
54 insertions(+)

Как видите, параметр --stat позволяет вывести под записью о каждой версии
список измененных файлов, их количество, а также количество добавленных в них
и удаленных из них строк. А в конце выводится сводная информация.
Еще одним крайне полезным параметром является --pretty. Он меняет формат
вывода информации. Есть несколько предустановленных вариантов. Параметр
oneline выводит каждый коммит в одну строку, что весьма удобно при просмотре
большого числа коммитов. Параметры short, full и fuller, практически не меняя
формат вывода, определяют его детализацию:
$ git log --pretty=oneline
ca82a6dff817ec66f44342007202690a93763949 changed the verison number
085bb3bcb608e1e8451d4b2432f8ecbe6306e7e7 removed unnecessary test
a11bef06a3f659402fe7563abf99ad00de2209e6 first commit

Наиболее интересен параметр format, позволяющий выводить записи журнала
в выбранном вами формате. Это особенно полезно при генерации данных для машинного анализа, ведь формат задается в явном виде, и вы можете быть уверены,
что при обновлениях Git он не изменится:
$ git log
ca82a6d 085bb3b a11bef0 -

--pretty=format:"%h Scott Chacon, 6 years
Scott Chacon, 6 years
Scott Chacon, 6 years

%an, %ar : %s"
ago : changed the version number
ago : removed unnecessary test
ago : first commit

Самые распространенные параметры форматирования перечислены в табл. 2.1.
Таблица 2.1. Полезные параметры команды git log --pretty=format
Параметр

Описание выводимых данных

%H

Хеш-код коммита

%h

Сокращенный хеш-код коммита

%T

Хеш-код дерева

%t

Сокращенный хеш-код дерева

%P

Хеш-код родительских коммитов

%p

Сокращенный хеш-код родительских коммитов

%an

Имя автора

Просмотр истории версий   51
Параметр

Описание выводимых данных

%ae

Электронная почта автора

%ad

Дата создания оригинала (формат учитывает –date= option)

%ar

Дата создания оригинала, в относительной форме

%cn

Имя создателя версии

%ce

Электронная почта создателя версии

%cd

Дата создания версии

%cr

Дата создания версии в относительном формате

%s

Комментарий

Возможно, у вас появился вопрос, в чем разница между автором (author) ­и созда­
телем версии (committer). Автор — это человек, создавший файл, в то время как
создателем версии называется тот, кто внес в авторскую версию правки и сохранил их. Так что если вы послали в проект некое исправление, а один из основных разработчиков применил его, вы оба останетесь в истории: вы — как автор,
а раз­работчик — как создатель версии. Более подробно это отличие рассмотрено
в главе 5.
Параметры oneline и format особенно полезны в сочетании с другим параметром
команды log, который называется --graph. Он добавляет небольшой ASCII-граф
с историей ветвлений и слияний:
$ git log --pretty=format:"%h %s" --graph
* 2d3acf9 ignore errors from SIGCHLD on trap
* 5e3ee11 Merge branch 'master' of git://github.com/dustin/grit
|\
| * 420eac9 Added a method for getting the current branch.
* | 30e367c timeout code and tests
* | 5a09431 add timeout protection to grit
* | e1193f8 support for heads with slashes in them
|/
* d6016bc require time for xmlschema
* 11d191e Merge branch 'defunkt' into local

В следующей главе, когда мы начнем рассматривать ветвления и слияния, вывод
этой команды будет выглядеть куда интереснее.
Мы рассмотрели только самые простые параметры форматирования вывода команды git log, а их существует великое множество. В табл. 2.2 представлены как уже
знакомые вам варианты, так и другие полезные параметры с описанием их влияния
на вывод команды log.

52    Глава 2 • Основы Git
Таблица 2.2. Распространенные параметры команды git log
Параметр

Описание

-p

Показывает изменения, внесенные в каждую версию

--stat

Показывает статистику измененных файлов в каждом
коммите

--shortstat

Показывает только строку с изменениями/вставками/
удалениями от команды --stat

--name-only

Показывает список измененных файлов после информации
о коммите

--name-status

Показывает список измененных файлов с информацией
о добавлении/изменении/удалении

--abbrev-commit

Показывает только первые несколько символов контрольной
суммы SHA-1 вместо всех 40

--relative-date

Показывает дату не в полном, а в относительном формате
(например, «2 недели назад»)

--graph

Показывает ASCII-граф истории ветвлений и слияний вместе
с выводом команды log

--pretty

Показывает коммиты в альтернативном формате. Возможны
параметры oneline, short, full, fuller и format (с указанием
вашей версии формата)

Ограничение вывода команды log
Команда git log обладает не только параметрами форматирования вывода, но
и ограничивающими параметрами, дающими возможность выводить на экран
только часть коммитов. Один из этих параметров вы уже встречали — это был
параметр -2, благодаря которому в вывод команды попали только два последних
коммита. По сути, вы можете указать параметр -n, где n — это количество выводимых коммитов. На практике этот параметр применяется не очень часто, так как
по умолчанию Git пропускает результат работы команды через сценарий постраничного вывода и выводит на экран только первую страницу.
Более полезны параметры, устанавливающие ограничения по времени, такие как
--since и --until. Вот пример команды, которая выводит список зафиксированных
за последние две недели версий:
$ git log --since=2.weeks

Просмотр истории версий   53

Эта команда работает с разными форматами: можно указать как точную дату,
­например «2008-01-15», так и относительную, то есть «2 года, 1 день и 3 минуты
назад».
Еще существует возможность фильтрации списка коммитов по какому-либо критерию. Параметр --author позволяет увидеть версии, созданные определенным автором, а параметр --grep дает возможность искать в сообщениях фиксации ключевые
слова. Для одновременного поиска по этим критериям следует добавлять параметр
--all-match, в противном случае в результаты попадет все то, что удовлетворяет
либо первому, либо второму критерию.
Еще одним крайне полезным фильтром является параметр -S, принимающий в
­ качестве аргумента строку. С этим параметром команда выдает только те коммиты,
изменения в которых включают добавление или удаление этой строки. К примеру,
для поиска последней версии, в которую добавлялась или из которой удалялась
ссылка на некую функцию, можно написать:
$ git log --Sfunction_name

Последним крайне полезным параметром команды git log, исполняющим роль
фильтра, является путь. Указание имени определенной папки или файла ограничивает вывод команды только версиями, в которых в данные файлы вносились
изменения. Этот параметр всегда фигурирует последним и обычно предваряется
двойным дефисом (--), отделяющим путь от остальных параметров.
В табл. 2.3 перечислены эти и некоторые другие часто употребляемые параметры.
Таблица 2.3. Параметры, ограничивающие вывод команды git log
Параметр

Описание

-(n)

Показывает только последние n коммитов

--since, --after

Показывает только коммиты, внесенные после указанной
даты

--until, --before

Показывает только коммиты, внесенные до указанной даты

--author

Показывает только коммиты определенного автора

--committer

Показывает только коммиты, внесенные определенным
участником

--grep

Показывает только коммиты с сообщением фиксации,
содержащим указанную строку

-S

Показывает только коммиты, в которых добавленный
или удаленный код совпадает с указанной строкой

54    Глава 2 • Основы Git
Вот каким образом можно посмотреть в истории исходного Git-кода коммиты
пользователя Junio Hamano, вносящие изменения в тестовые файлы и не подвергавшиеся слиянию в течение октября 2008 года:
$ git log --pretty="%h - %s" --author=gitster --since="2008-10-01" \
--before="2008-11-01" --no-merges -- t/
5610e3b - Fix testcase failure when extended attributes are in use
acd3b9e - Enhance hold_lock_file_for_{update,append}() API
f563754 - demonstrate breakage of detached checkout with symbolic link HEAD
d1a43f2 - reset --hard/read-tree --reset -u: remove unmerged new paths
51a94af - Fix "checkout --track -b newbranch" on detached HEAD
b0ad11e - pull: allow "git pull origin $something:$current_branch" into an unborn
branch

Из практически 40 000 коммитов в истории исходного Git-кода эта команда выводит всего 6, подпадающих под указанный критерий.

Отмена изменений
Необходимость отмены внесенных изменений может возникнуть на любой стадии
проекта. В этом разделе мы рассмотрим несколько базовых инструментов, позволяющих это сделать. Однако здесь следует быть крайне осторожным, ведь после
отмены далеко не всегда существует возможность вернуться в предшествующее состояние. Это одна из немногих ситуаций при работе с Git, когда неверные действия
могут привести к потере результатов вашего труда.
Необходимость отмены чаще всего возникает при слишком ранней фиксации изменений, когда вы забыли добавить в коммит какие-то файлы или ошиблись с сообщением фиксации. Для повторного сохранения версии в такой ситуации можно
воспользоваться параметром --amend:
$ git commit –amend

Эта команда берет область индексирования и включает в коммит всю обнаруженную
там информацию. Если после последней фиксации версии вы не внесли никаких
изменений (к примеру, эта команда была запущена непосредственно после предыдущего коммита), снимок состояния останется таким же, как и был, изменится
только сообщение фиксации.
Откроется тот же самый редактор сообщения фиксации, но с уже введенной туда
версией сообщения к предыдущему коммиту. Вы можете обычным образом отредактировать это сообщение, но предшествующий коммит в результате будет
переписан.
Скажем, если после фиксации очередной версии вы обнаружили, что забыли проиндексировать изменения в одном из файлов, который планировалось включить
в новый коммит, можно сделать так:

Отмена изменений   55
$ git commit -m 'изначальный коммит'
$ git add forgotten_file
$ git commit --amend

В итоге у вас останется единственный коммит — второй коммит заменит результат
первого.

Отмена индексирования
Следующие два раздела демонстрируют приемы управления изменениями в области
индексирования и рабочей папке. Что замечательно, команда, задающая состояние
этих двух областей, напоминает, каким образом можно отменить внесенные изменения. Предположим, вы отредактировали два файла и хотели бы зафиксировать
их в двух разных коммитах, но случайно набрали команду git add *, что привело
к их одновременному индексированию. Как отменить индексирование одного из
них? Это покажет команда git status:
$ git add .
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
renamed:
modified:

README.md -> README
benchmarks.rb

Сразу под текстом Changes to be committed написано, что для отмены индексирования следует воспользоваться командой git reset HEAD . Последуем
этому совету и отменим индексирование файла benchmarks.rb:
$ git reset HEAD benchmarks.rb
Unstaged changes after reset:
M benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
renamed: README.md -> README
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Эта команда выглядит несколько странно, но все же работает. Теперь файл
benchmarks.rb фигурирует как измененный, но непроиндексированный.
Вот и все, что вам на данный момент требуется знать о команде git reset. Более подробно она рассматривается далее, где вы познакомитесь с дополнительными вариантами ее применения, позволяющими делать очень интересные вещи.

56    Глава 2 • Основы Git
ПРИМЕЧАНИЕ
С параметром --hard команда git reset может привести к опасным последствиям,
так как в этом случае затрагиваются файлы в рабочей папке. Без этого параметра
команда git reset совершенно безопасна, так как затрагивает только область индексирования.

Отмена внесенных в файл изменений
Что делать, если вы внезапно поняли, что не хотите сохранять внесенные в файл
benchmarks.rb изменения? Как быстро отменить их и вернуть файл в состояние,
в котором он находился до последней фиксации (или до первоначального клонирования или другого действия, после которого файл попал в рабочий каталог)? Нам
на помощь снова приходит команда git status. В последнем выводе неиндексированная область выглядела так:
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: benchmarks.rb

Здесь достаточно четко сказано, как отменить сделанные изменения. Последуем
данному совету:
$ git checkout -- benchmarks.rb
$ git status
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
renamed: README.md -> README

Как видите, изменения были отменены.
ВНИМАНИЕ
Важно понимать степень опасности команды git checkout -- [файл]. Все внесенные
в файл изменения пропадают — вы просто копируете в этот файл содержимое другого
файла. Никогда не пользуйтесь этой командой, если не уверены, что данный файл вам
больше не понадобится.

Ветвление и скрытие — операции, которые позволят сохранить внесенные в файл
изменения, но временно убрать его с глаз, — рассмотрены в главе 3. Как правило,
они являются оптимальным решением данной задачи.
Помните, что в Git все, что входит в коммиты, почти всегда поддается восстановлению, даже коммиты из удаленных веток или переписанные с помощью параметра

Удаленные репозитории   57

--amend (см. раздел, посвященный восстановлению данных). Но все, что не было

зафиксировано, при потере, скорее всего, пропадет навсегда.

Удаленные репозитории
Для совместной работы над проектами Git требуются навыки управления удаленными репозиториями. Удаленные репозитории представляют собой версии проекта,
хранимые в Интернете или где-то в сети. Их может быть несколько, и каждый в
общем случае доступен вам только для чтения или же для чтения и записи. Вы
должны уметь отправлять данные в удаленный репозиторий и извлекать их оттуда
каждый раз, когда требуется обменяться результатами работы. Кроме того, вы должны знать, как добавить удаленный репозиторий, как удалить репозиторий, который
больше не используется, как управлять различными удаленными ветками и делать
их отслеживаемыми и неотслеживаемыми, и многое другое. В данном разделе мы
рассмотрим некоторые из этих приемов.

Отображение удаленных репозиториев
Просмотр уже настроенных удаленных серверов осуществляется командой git
remote. Она дает список коротких имен для всех указанных вами областей удаленной работы. Если репозиторий был клонирован, вы должны увидеть по крайней
мере источник, то есть имя, которое Git по умолчанию присваивает клонируемому
серверу:
$ git clone https://github.com/schacon/ticgit
Cloning into 'ticgit'...
remote: Reusing existing pack: 1857, done.
remote: Total 1857 (delta 0), reused 0 (delta 0)
Receiving objects: 100% (1857/1857), 374.35 KiB | 268.00 KiB/s, done.
Resolving deltas: 100% (772/772), done.
Checking connectivity... done.
$ cd ticgit
$ git remote
origin

Параметр -v позволяет увидеть URL-адреса, которые Git хранит для сокращенного имени, используемого при чтении из данного удаленного репозитория и при
записи в него:
$ git remote -v
Origin https://github.com/schacon/ticgit (fetch)
Origin https://github.com/schacon/ticgit (push)

Если репозиториев несколько, они выводятся списком. Скажем, репозиторий
с несколькими удаленными копиями для совместной работы с коллегами может
выглядеть следующим образом.

58    Глава 2 • Основы Git
$ cd grit
$ git remote
bakkdoor
bakkdoor
cho45
cho45
defunkt
defunkt
koke
koke
origin
origin

-v
https://github.com/bakkdoor/grit (fetch)
https://github.com/bakkdoor/grit (push)
https://github.com/cho45/grit (fetch)
https://github.com/cho45/grit (push)
https://github.com/defunkt/grit (fetch)
https://github.com/defunkt/grit (push)
git://github.com/koke/grit.git (fetch)
git://github.com/koke/grit.git (push)
git@github.com:mojombo/grit.git (fetch)
git@github.com:mojombo/grit.git (push)

Это означает, что мы легко можем скачать себе изменения, внесенные любым
из этих пользователей. При этом у нас может быть допуск на запись в один или
не­сколько репозиториев из списка, хотя представленный код данную деталь не
­показывает.
Обратите внимание, что эти удаленные репозитории пользуются разными протоколами; впрочем, детально эта тема рассматривается в главе 4.

Добавление удаленных репозиториев
Ранее уже не раз упоминался и демонстрировался процесс добавления удаленных
репозиториев, но теперь мы рассмотрим его в явном виде. Чтобы добавить такой
репозиторий под коротким именем, которое упростит дальнейшие обращения к
нему, используйте команду git remote add [сокращенное имя] [url]. Вот как добавить репозиторий коллеги Пола:
$ git remote
origin
$ git remote add pb https://github.com/paulboone/ticgit
$ git remote -v
origin
https://github.com/schacon/ticgit (fetch)
origin
https://github.com/schacon/ticgit (push)
pb
https://github.com/paulboone/ticgit (fetch)
pb
https://github.com/paulboone/ticgit (push)

Теперь вместо полного URL-адреса в командную строку можно вводить имя pb.
К примеру, для скачивания всей информации, которая есть у коллеги, но отсутствует
у вас, используйте команду git fetch pb:
$ git fetch pb
remote: Counting objects: 43, done.
remote: Compressing objects: 100% (36/36), done.
remote: Total 43 (delta 10), reused 31 (delta 5)
Unpacking objects: 100% (43/43), done.
From https://github.com/paulboone/ticgit
* [new branch]
master
-> pb/master
* [new branch]
ticgit
-> pb/ticgit

Удаленные репозитории   59

Ветка master этого коллеги теперь доступна вам локально как pb/master. Вы можете
выполнить ее слияние с одной из ваших веток или перейти в нее, если требуется
просто проверить ее содержимое. (Ветви и работа с ними подробно рассматриваются в главе 3.)

Извлечение данных из удаленных репозиториев
Как вы уже видели, извлечение данных из удаленных проектов выполняется такой
командой:
$ git fetch [имя удаленного репозитория]

Эта команда связывается с удаленным проектом и извлекает оттуда все пока
­отсутствующие у вас данные. После этого у вас должны появиться ссылки на
­все ветки удаленного проекта, которые можно подвергнуть слиянию или про­
смотреть.
При клонировании данная команда автоматически добавляет удаленный репозиторий под именем «origin». Соответственно команда git fetch origin извлекает
все, что появилось на этом сервере после его клонирования (или после момента
последнего извлечения информации). Важно понимать, что команда git fetch
помещает все данные в ваш локальный репозиторий, — она не выполняет автоматическое слияние с ветками, с которыми вы работаете в данный момент, и вообще
никак не затрагивает эти ветки. Слияние вы выполните вручную, как только в этом
возникнет необходимость.
Если же у вас есть ветка, настроенная на слежение за какой-то удаленной веткой (подробно эта операция рассматривается в главе 3), команда git pull бу­
дет автоматически­извлекать информацию из удаленной ветки и выполнять слияние с текущей веткой. В некоторых случаях такой порядок вещей оказывается
проще и удобнее; кроме того, по умолчанию команда git clone автоматически
настраивает вашу локальную ветку master на слежение за удаленной веткой
master (она может иметь и другое имя) на сервере, с которого вы выполняли
клониро­вание. В общем случае команда git pull извлекает данные с сервера,
который вы клонировали, и автоматически пытается слить их с вашим текущим
рабочим кодом.

Отправка данных в удаленный репозиторий
Чтобы поделиться результатами своего труда, их нужно отправить в репозиторий.
Это делается простой командой git push [имя удаленного сервера] [ветка]. Для
отправки ветки master на сервер origin (еще раз напоминаем, что в процессе клонирования эти имена присваиваются автоматически) следует написать:
$ git push origin master

60    Глава 2 • Основы Git
Команда сработает только при условии, что клонирование осуществлялось с сервера, где у вас есть доступ на запись, и за это время никто не отправлял туда свои
данные. Если вы выполнили клонирование одновременно с другим пользователем
и он уже отправил результаты своей работы на сервер, ваша попытка отправки
данных окончится неудачей. Вам сначала нужно скачать все добавленное этим
пользователем и встроить это в свои данные, и только после этого появится возможность воспользоваться командой push. Более подробно процесс отправки данных
на удаленные серверы рассматривается в главе 3.

Просмотр удаленных репозиториев
Для получения дополнительной информации о конкретном удаленном репозитории применяется команда git remote show [имя удаленного сервера]. Вставив
в команду имя origin, вы получите примерно такой результат:
$ git remote show origin
* remote origin
Fetch URL: https://github.com/schacon/ticgit
Push URL: https://github.com/schacon/ticgit
HEAD branch: master
Remote branches:
master
tracked
dev-branch
tracked
Local branch configured for 'git pull':
master merges with remote master
Local ref configured for 'git push':
master pushes to master (up to date)

Выводятся URL-адрес удаленного репозитория и информация об отслеживаемых
ветках. Команда услужливо сообщает, что если, находясь в ветке master, вы запускаете команду git pull, ветка master с удаленного сервера будет автоматически
слита с вашей сразу же после скачивания всех необходимых данных. Кроме того,
она выводит на экран список всех скачанных ею ссылок.
Это был простой пример, не имеющий практического смысла. Но при более интенсивной работе с Git может возникнуть ситуация, когда команда git remote show
выведет на экран большее количество информации:
$ git remote show origin
* remote origin
URL: https://github.com/my-org/complex-project
Fetch URL: https://github.com/my-org/complex-project
Push URL: https://github.com/my-org/complex-project
HEAD branch: master
Remote branches:
master
tracked
dev-branch
tracked
markdown-strip
tracked
issue-43
new (next fetch will store in remotes/origin)

Теги   61
issue-45
new (next fetch will store in remotes/origin)
refs/remotes/origin/issue-11 stale (use 'git remote prune' to remove)
Local branches configured for 'git pull':
dev-branch merges with remote dev-branch
master merges with remote master
Local refs configured for 'git push':
dev-branch
pushes to dev-branch
(up to date)
markdown-strip
pushes to markdown-strip
(up to date)
master
pushes to master
(up to date)

Здесь сообщается, какая именно ветка будет автоматически отправлена на сервер
при выполнении команды git push. Также в выводе команды отображены пока
отсутствующие у вас ветки с удаленного сервера, ветки, которые есть у вас, но
удалены с сервера, и наборы веток, которые автоматически подвергаются слиянию
при выполнении команды git pull.

Удаление и переименование
удаленных репозиториев
Переименование ссылок осуществляется командой git remote rename, меняющей
сокращенные имена удаленных репозиториев. К примеру, вот как выглядит присвоение репозиторию pb имени paul:
$ git remote rename pb paul
$ git remote
origin
paul

Имеет смысл упомянуть, что эта команда умеет менять и имена удаленных веток.
Теперь к ветке pb/master нужно обращаться по имени paul/master.
Если по какой-то причине вы хотите удалить ссылку на удаленный репозиторий (­ например, вы поменяли сервер, больше не используете конкретное зеркало
или участник проекта перестал вносить в него вклад), используйте команду git
remote rm:
$ git remote rm paul
$ git remote
origin

Теги
Подобно большинству VCS, Git позволяет помечать определенные моменты истории. Как правило, эту функциональность задействуют для пометки выходящих
версий (v1.0 и т. п.). В этом разделе показано, как посмотреть все доступные теги,
как создавать новые, какие типы тегов существуют.

62    Глава 2 • Основы Git

Вывод списка тегов
Для просмотра списка доступных тегов применяется очевидная команда git tag:
$ git tag
v0.1
v1.3

Теги выводятся в алфавитном порядке; порядок их вывода никакого значения не имеет.­
Кроме того, искать теги можно по шаблону. Например, пусть репозиторий Git содержит более 500 тегов. Предположим, вас интересуют только выпуски 1.8.5:
$ git tag -l ‚v1.8.5*'
v1.8.5
v1.8.5-rc0
v1.8.5-rc1
v1.8.5-rc2
v1.8.5-rc3
v1.8.5.1
v1.8.5.2
v1.8.5.3
v1.8.5.4
v1.8.5.5

Создание тегов
В Git используются два основных типа тегов: легковесные и снабженные ком­
ментарием.­
Легковесный тег (lightweight tag) во многом напоминает не меняющуюся ветку —
это просто указатель на конкретный коммит. А вот теги с комментариями (annotated
tags) хранятся в базе данных Git как полноценные объекты. Они обладают контрольной суммой; содержат имя человека, поставившего тег, адрес его электронной почты
и дату создания; снабжены комментарием; могут быть подписаны и проверены в
программе GNU Privacy Guard (GPG). Обычно рекомендуется создавать именно
теги с комментариями, чтобы у вас была вся эта информация, но если нужно сделать
временный тег или по какой-то причине вы не хотите хранить все эти сведения,
можно обойтись и легковесными тегами.

Теги с комментариями
Создать в Git тег, снабженный комментарием, очень легко. Проще всего добавить
параметр -a к команде tag:
$ git tag -a v1.4 -m 'my version 1.4'
$ git tag
v0.1
v1.3
v1.4

Теги   63

Параметр -m задает сообщение, которое будет храниться вместе с тегом. Если вы не
укажете это сообщение, Git запустит редактор, чтобы вы смогли его ввести.
Для просмотра данных тега вместе с помеченным им коммитом служит команда
git show:
$ git show v1.4
tag v1.4
Tagger: Ben Straub
Date:
Sat May 3 20:19:12 2014 -0700
my version 1.4
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number

Перед информацией о коммите выводятся данные человека, поставившего тег, дата
пометки коммита и текст комментария.

Легковесные теги
Другим средством пометки коммитов являются легковесные теги. По сути, это сохраненная в файле контрольная сумма коммита — больше никакой информации
они не содержат. Для создания легковесного тега достаточно опустить параметры
-a, -s и -m:
$ git tag v1.4-lw
$ git tag
v0.1
v1.3
v1.4
v1.4-lw
v1.5

На этот раз примененная к тегу команда git show не выводит никакой дополнительной информации. На экране появляется только помеченный коммит:
$ git show v1.4-lw
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number

Расстановка тегов постфактум
Тегами можно помечать и сделанные ранее коммиты. Предположим, что история
зафиксированных состояний выглядит так:

64    Глава 2 • Основы Git
$ git log --pretty=oneline
15027957951b64cf874c3557a0f3547bd83b3ff6
a6b4c97498bd301d84096da251c98a07c7723e65
0d52aaab4479697da7686c15f77a3d64d9165190
6d52a271eda8725415634dd79daabbc4d9b6008e
0b7434d86859cc7b8c3d5e1dddfed66ff742fcbc
4682c3261057305bdd616e23b64b0857d832627b
166ae0c4d3f420721acbb115cc33848dfcc2121a
9fceb02d0ae598e95dc970b74767f19372d61af8
964f16d36dfccde844893cac5b347e7b3d44abbc
8a5cbc430f1a9c3d00faaeffd07798508422908a

Merge branch 'experiment'
beginning write support
one more thing
Merge branch 'experiment'
added a commit function
added a todo file
started write support
updated rakefile
commit the todo
updated readme

Также предположим, что вы забыли снабдить тегом версию проекта v1.2, соответствующую коммиту, упомянутому как «updated rakefile». Это можно сделать и позже,
указав в конце команды контрольную сумму коммита (или ее часть):
$ git tag -a v1.2 9fceb02

Теперь проверим наличие тега:
$ git tag
v0.1
v1.2
v1.3
v1.4
v1.4-lw
v1.5
$ git show v1.2
tag v1.2
Tagger: Scott Chacon
Date:
Mon Feb 9 15:32:16 2009 -0800
version 1.2
commit 9fceb02d0ae598e95dc970b74767f19372d61af8
Author: Magnus Chacon
Date:
Sun Apr 27 20:43:35 2008 -0700
updated rakefile
...

Обмен тегами
По умолчанию команда git push не отправляет теги на удаленные серверы. Созданные вами теги нужно отправлять на сервер общего доступа отдельно. Этот процесс
напоминает совместное использование удаленных веток — вы делаете это командой
git push origin [имя тега].
$ git push origin v1.5
Counting objects: 14, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (12/12), done.
Writing objects: 100% (14/14), 2.05 KiB | 0 bytes/s, done.
Total 14 (delta 3), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new tag]
v1.5 -> v1.5

Псевдонимы в Git   65

Когда требуется переслать сразу много тегов, добавляйте к команде git push
параметр --tags. В результате на удаленный сервер будут отправлены все теги,
которых там пока нет.
$ git push origin --tags
Counting objects: 1, done.
Writing objects: 100% (1/1), 160 bytes | 0 bytes/s, done.
Total 1 (delta 0), reused 0 (delta 0)
To git@github.com:schacon/simplegit.git
* [new tag]
v1.4 -> v1.4
* [new tag]
v1.4-lw -> v1.4-lw

Теперь пользователь, который клонирует ваш репозиторий или скачает из него
данные, получит и все ваши теги.

Псевдонимы в Git
Перед тем как закончить эту главу, посвященную основам Git, дадим один совет,
который сделает вашу работу с системой Git проще, удобнее и привычнее. Речь
идет о псевдонимах (aliases).
При неполном вводе команды Git не пытается догадаться, что за команду вы имели в виду. Но если мысль о вводе длинных команд вас не привлекает, команда git
config позволяет легко создать псевдоним для любой из них. Вот пара примеров
ее применения:
$
$
$
$

git
git
git
git

config
config
config
config

--global
--global
--global
--global

alias.co
alias.br
alias.ci
alias.st

checkout
branch
commit
status

Теперь вместо команды git commit достаточно будет ввести git ci. По мере освоения Git вам, скорее всего, придется часто пользоваться и другими командами; не
стесняйтесь создавать для них псевдонимы.
Кроме того, эта техника позволяет создавать команды, которых, с вашей точки зрения, не хватает. К примеру, чтобы устранить функциональную проблему, с которой
мы столкнулись при отмене индексирования файла, можно добавить псевдоним
unstage:
$ git config --global alias.unstage 'reset HEAD --'

После этого следующие команды станут эквивалентными:
$ git unstage fileA
$ git reset HEAD fileA

В таком виде все выглядит понятнее. Также пользователи частенько добавляют
команду, выводящую последние коммиты:
$ git config --global alias.last 'log -1 HEAD'

66    Глава 2 • Основы Git
Теперь для просмотра последнего коммита достаточно написать:
$ git last
commit 66938dae3329c7aebe598c2246a8e6af90d04646
Author: Josh Goebel
Date: Tue Aug 26 19:48:51 2008 +0800
test for current head
Signed-off-by: Scott Chacon

Можно сказать, что Git просто заменяет новые команды созданным вами псевдонимом. А что делать в случае, когда нужно запустить внешнюю команду, а не
команду, встроенную в Git? Такую команду следует начинать с символа !. Этот
прием часто применяется при написании собственных инструментов для работы
с Git-репозиторием. Вот пример создания псевдонима для команды git visual,
служащей для запуска gitk:
$ git config --global alias.visual "!gitk"

Заключение
Итак, вы научились выполнять все базовые локальные операции в Git: создавать
или клонировать репозитории, вносить изменения, индексировать и фиксировать
их, а также просматривать историю всех сделанных в репозитории изменений.
­Пришла пора познакомиться с самым важным преимуществом Git — моделью
ветвления.

3

Ветвления в Git

Почему каждая VCS в той или иной форме поддерживает ветвление? Термин
ветвление (branching) означает отклонение от основной линии разработки, после
которого работа перестает затрагивать эту самую основную линию. Во многих VCS
это крайне сложный процесс, зачастую требующий создания новой копии папки
с исходным кодом, что в случае больших проектов занимает изрядное время.
Некоторые называют модель ветвлений в Git основным преимуществом этой системы, однозначно выделяющим его из ряда прочих VCS. В чем же состоит основное
отличие? Способ создания новых веток в Git невероятно упрощен, что делает эту
процедуру практически мгновенной, и так же быстро позволяет переходить от
одной ветки к другой и обратно. В отличие от других VCS в Git поддерживается
рабочий процесс с частыми ветвлениями и слияниями, порой осуществляемыми
несколько раз в день. Понимание этой конструктивной особенности и умение ею
пользоваться дает вам в руки уникальный и мощный инструмент, полностью меняющий способы разработки.

Суть ветвления
Чтобы понять, каким способом в Git выполняется ветвление, нужно вернуться немного назад и вспомнить, как именно Git сохраняет данные. В главе 1 мы говорили
о том, что Git хранит не последовательности изменений, а наборы состояний.
При фиксации очередного состояния Git сохраняет объект-коммит, содержащий
указатель на снимок индексированного содержимого. Еще этот объект содержит
имя и адрес электронной почты автора, введенный вами комментарий и указатели

68    Глава 3 • Ветвления в Git
на коммит или коммиты, которые являются прямыми предками текущего. Начальный коммит предков не имеет, у обычного коммита один предок, а у коммитов,
полученных слиянием двух и более веток, целый набор предков.
Для наглядности предположим, что у вас есть папка с тремя файлами, которые
требуется проиндексировать и зафиксировать. При индексировании файлов для
каждого из них вычисляется контрольная сумма (хеш SHA-1, упоминавшийся в
главе 1), их версии сохраняются в Git-репозиторий (Git трактует их как массивы
двоичных данных), а контрольные суммы добавляются в область индексирования:
$ git add README test.rb LICENSE
$ git commit -m 'начальный коммит моего проекта'

При создании коммита командой git commit Git вычисляет контрольную сумму
каждой выложенной папки (в нашем случае все ограничивается корневым каталогом
проекта) и сохраняет эти объекты-деревья в Git-репозиторий. Затем Git создает
объект-коммит с метаданными и указателем на корень дерева проекта. Все это дает
возможность при необходимости воссоздать снимок состояния.
В настоящее время ваш Git-репозиторий содержит пять объектов: один массив
двоичных данных (blob) с содержимым трех файлов, одно дерево (tree), отображающее содержимое папки и указывающее, какие имена файлов сохранены как
массивы двоичных данных, и один коммит с указателем на корень дерева и все
свои метаданные (рис. 3.1).
5b1d3
blob size
== Testing library
This library is used to test
Ruby projects

98ca9
commit
tree
author
committer

size
92ec2
Scott
Scott

92ec2

911e7

tree size
blob 5b1d3 README
blob 911e7 LICENSE
blob cba0a test.rb

blob size
The MIT License
Copyright (c) 2008 Scott Chacon
Permission is hereby granted,
free of charge, to any person

The initial commit of my project

cba0a
blob size
require 'logger'
require 'test/unit'
class Test::Unit::TestCase

Рис. 3.1. Коммит и его дерево

Суть ветвления   69

Если после внесения изменений снова произвести фиксацию, новый коммит будет
содержать указатель на коммит, сделанный непосредственно перед ним (рис. 3.2).
98ca9
commit
tree
parent
author
committer

size
92ec2
Scott
Scott

34ac2
commit
tree
parent
author
committer

f30ab

size
184ca
98ca9
Scott
Scott

commit
tree
parent
author
committer

size
0de24
34ac2
Scott
Scott

The initial commit of my project

Fixed bug #1328 - stack overflow
under certain conditions

add feature #32 - ability to add new
formats to the central interface

Снимок A

Снимок B

Снимок C

Рис. 3.2. Коммиты и их предки

Ветка в Git представляет собой всего лишь легкий, подвижный указатель на один
из этих коммитов. По умолчанию в Git ей присваивается имя master. Как только
вы начинаете создавать коммиты, появляется ветка master, указывающая на последнее зафиксированное вами состояние. При каждой следующей фиксации этот
указатель автоматически смещается вперед (рис. 3.3).
ПРИМЕЧАНИЕ
Ветка master в Git ничем не отличается от остальных. Но она присутствует практически в каждом репозитории, потому что создается командой git init по умолчанию,
а большинство пользователей не утруждают себя переименованием.

HEAD

v1.0

master

98ca9

34ac2

f30ab

Снимок A

Снимок B

Снимок C

Рис. 3.3. Ветка и история ее коммитов

70    Глава 3 • Ветвления в Git

Создание новой ветки
Что происходит при создании новой ветки? Появляется новый указатель, который
можно перемещать. Предположим, вы создаете ветку testing. Эта операция выполняется командой git branch:
$ git branch testing

Появится новый указатель на ваш текущий коммит (рис. 3.4).
master

98ca9

34ac2

f30ab

testing

Рис. 3.4. Две ветки, указывающие на один и тот же набор коммитов

Откуда Git узнает, какой именно коммит является текущим? Для этого он хранит
специальный указатель HEAD. Имейте в виду, что в данном случае смысл указателя
HEAD не такой, как в других привычных вам VCS, например Subversion или CVS.
В Git он указывает на локальную ветку, в которой вы находитесь в данный момент
(рис. 3.5). Пока это все еще ветка master. Команда git branch всего лишь создала
новую ветку, но не перевела вас в нее.
HEAD

master

98ca9

34ac2

f30ab

testing

Рис. 3.5. Указатель HEAD нацелен на текущую ветку

Суть ветвления   71

Узнать, куда именно нацелены указатели веток, позволяет команда git log ­с параметром --decorate.
$ git
f30ab
34ac2
98ca9

log --oneline --decorate
(HEAD, master, testing) add feature #32 - ability to add new
fixed bug #1328 - stack overflow under certain conditions
initial commit of my project

Легко увидеть, что ветки master и testing находятся непосредственно рядом
с коммитом f30ab.

Смена веток
Переход на существующую ветку реализует команда git checkout. Перейдем на
ветку testing:
$ git checkout testing

Теперь указатель HEAD нацелен на ветку testing (рис. 3.6).

master

98ca9

34ac2

f30ab

testing

HEAD

Рис. 3.6. Указатель HEAD нацелен на текущую ветку

Какое все это имеет значение? Давайте еще раз зафиксируем состояние:
$ vim test.rb
$ git commit -a -m 'внесено изменение'

Оказывается, ваша ветка testing сместилась вперед, в то время как ветка master
осталась связанной с коммитом, текущим на момент перехода на другую ветку, при
помощи команды git checkout (рис. 3.7).

72    Глава 3 • Ветвления в Git
master

98ca9

34ac2

f30ab

87ab2

testing

HEAD

Рис. 3.7. Ветка с указателем HEAD сместилась вперед после нового коммита

Вернемся на ветку master (рис. 3.8):
$ git checkout master

HEAD

master

98ca9

34ac2

f30ab

87ab2

testing

Рис. 3.8. Смещение указателя HEAD при переключении

Эта команда выполняет два действия. Она возвращает указатель HEAD ветке master
и приводит файлы в рабочей папке в состояние, зафиксированное в этой ветке. Это
означает, что все изменения, вносимые после этого момента, будут ответвляться от
старой версии проекта. По сути, так вы отменяете результаты работы, сделанной
в ветке testing, и идете дальше в другом направлении.
Внесем несколько изменений и зафиксируем их:
$ vim test.rb
$ git commit -a -m 'внесены дополнительные изменения'

Суть ветвления   73

СМЕНА ВЕТОК МЕНЯЕТ ФАЙЛЫ В РАБОЧЕЙ ПАПКЕ
Важно понимать, что переход на другую ветку сопровождается сменой файлов в рабочей
папке. При возвращении в более старую ветку содержимое рабочей папки приобретет
тот вид, который оно имело перед последним коммитом в этой ветке. Если система Git не
сможет вернуть файлы в это состояние, она просто не даст вам выполнить переключение.

История проекта разветвилась. Вы создали ветку, перешли в нее, поработали немного, вернулись в основную ветку и снова произвели некие действия. Внесенные
в этих двух случаях изменения оказались принадлежащими разным веткам: вы
можете переходить из одной ветки в другую, а при необходимости объединять их
(рис. 3.9). И все это при помощи простых команд branch, checkout и commit.
HEAD

master

87ab2
98ca9

34ac2

f30ab
c2b9e

testing

Рис. 3.9. Расходящаяся история

Суть происходящего демонстрирует и команда git log. В форме git log --oneline
--decorate –graph --all она выводит историю коммитов, показывая места расположения указателей и точки расхождения.
$ git log --oneline --decorate --graph --all
* c2b9e (HEAD, master) made other changes
| * 87ab2 (testing) made a change
|/
* f30ab add feature #32 - ability to add new formats to the
* 34ac2 fixed bug #1328 - stack overflow under certain conditions
* 98ca9 initial commit of my project

Так как ветка в Git представляет собой обычный файл с 40 символами контрольной
суммы SHA-1 коммита, на который она указывает, операции создания и удаления

74    Глава 3 • Ветвления в Git
веток практически не требуют ресурсов. Создать новую ветку так же просто, как
записать в файл 41 байт (40 символов и знак переноса строки).
Это разительно отличается от механизма ветвления в большинстве VCS, требующего копирования всех файлов проекта в другую папку. В зависимости от размера
проекта эта операция может занять от нескольких секунд до нескольких минут, в то
время как в Git это моментальный процесс. Кроме того, благодаря записи предков
при фиксации состояний поиск базовой версии для слияния автоматически выполняется за нас и в общем случае элементарно реализуется на практике. Именно эти
конструктивные особенности поощряют разработчиков создавать ветки и активно
с ними работать.
Сейчас и вы увидите, как это все действует.

Основы ветвления и слияния
Простые случаи ветвления и слияния мы рассмотрим на схемах, которые могут
пригодиться вам при решении реальных задач. Последовательность действий будет
следующей:
1. Выполняем некие действия на сайте.
2. Создаем ветку для новой истории, над которой тоже нужно работать.
3. В этой ветке тоже производим некие действия.
А теперь предположим, что нам позвонили и сообщили о важной проблеме, требующей срочного решения. Поступаем следующим образом:
4. Переключаемся в производственную ветку.
5. Создаем ветку для решения проблемы.
6. После тестирования выполняем слияние побочной ветки и отправляем ее
в разработку.
7. Возвращаемся к первоначальной задаче и продолжаем работу.

Основы ветвления
В качестве исходных условий представим, что у вас уже есть пара коммитов проекта (рис. 3.10).
Вы решили приступить к работе над проблемой 53, фигурирующей в системе отслеживания ошибок вашей фирмы. Создать ветку и сразу перейти туда позволяет
команда git checkout с параметром -b (рис. 3.11):
$ git checkout -b iss53
Переход на новую ветку "iss53"

Основы ветвления и слияния   75

master

C0

C1

C2

Рис. 3.10. Простая история коммитов

master

C0

C1

C2

iss53

Рис. 3.11. Создание указателя на новую ветку

Это сокращенная запись для команд:
$ git branch iss53
$ git checkout iss53

Вы работаете над сайтом и несколько раз фиксируете состояние. При этом, как
показано на рис. 3.12, ветка iss53 смещается вперед, так как вы в ней находитесь
(то есть именно на нее нацелен указатель HEAD):
$ vim index.html
$ git commit -a -m 'добавлен новый нижний колонтитул [проблема 53]'

master

C0

C1

C2

C3

iss53

Рис. 3.12. Ветка iss53 в процессе работы сместилась вперед

76    Глава 3 • Ветвления в Git
Именно на этом этапе возникает звонок с сообщением о проблеме. Благодаря Git
вам не нужно выполнять развертывание исправлений вместе с внесенными в ветку
iss53 изменениями или ценой массы усилий отменять эти изменения перед тем,
как приступить к устранению проблемы. Достаточно вернуться в ветку master.
Однако при наличии в рабочей папке или в области индексирования незафиксированных изменений, конфликтующих с веткой, в которую вы хотите перейти,
система Git не позволит выполнить переключение. Перед переходом из ветки в
ветку лучше всего иметь чистое рабочее состояние. Это ограничение можно обойти
(например, скрыв или переписав коммит), но об этом мы поговорим чуть позже.
На данный же момент предположим, что все изменения зафиксированы и ничто
не мешает вам вернуться в ветку master:
$ git checkout master
Switched to branch 'master

Теперь рабочая папка проекта вернулась в то состояние, в котором она пребывала
перед началом работы над проблемой 53, и можно сконцентрироваться на решении
новой задачи (на рисунках она будет обозначаться как hotfix). Важно помнить, что
при смене веток Git возвращает рабочую папку в то состояние, которое она имела на
момент последнего коммита этой ветки. Добавление, удаление и изменение файлов
при этом происходит автоматически.
Итак, у вас есть ошибка, которую нужно исправить. Создадим ветку hotfix, с которой мы будем работать, пока не решим поставленную задачу (рис. 3.13):
$ git checkout -b hotfix
Switched to a new branch 'hotfix'
$ vim index.html
$ git commit -a -m 'fixed the broken email address'
[hotfix 1fb7853] fixed the broken email address
1 file changed, 2 insertions(+)

C0

C1

master

hotfix

C2

C4

C3

iss53

Рис. 3.13. Ветка hotfix базируется на ветке master

Основы ветвления и слияния   77

После того как проблемабудет решена, вы сможете произвести тестирование,
убедиться, что найденное решение работает, и объединить ветку hotfix с веткой
master, чтобы внедрить внесенные изменения в готовый код. Эта операция выполняется командой git merge:
$ git checkout master
$ git merge hotfix
Updating f42c576..3a0874c
Fast-forward
index.html | 2 ++
1 file changed, 2 insertions(+)

Обратите внимание на словосочетание «fast-forward» в описании результатов
слияния. Так как подвергшаяся слиянию ветка указывала на коммит, являющийся
предком текущего коммита, система Git просто сдвинула указатель вперед. Другими
словами, при попытке выполнить слияние с коммитом, которого можно достичь,
просматривая историю первого коммита, Git просто перемещает указатель вперед,
ведь расходящиеся изменения, требующие объединения друг с другом, в данном
случае отсутствуют — данный тип слияния называют перемоткой (fast-forward).
Теперь ваши изменения зафиксированы, на что указывает ветка master, и их можно
внедрить в рабочий код (рис. 3.14).
master

hotfix

C0

C1

C2

C4

C3

iss53

Рис. 3.14. После процедуры слияния ветки master и hotfix указывают
на один коммит

После внедрения важного исправления можно вернуться к прерванной работе.
Но сначала следует удалить ветку hotfix, так как она нам больше не понадобится, — на этот коммит уже указывает ветка master. Для этого достаточно добавить
параметр -d к команде git branch:

78    Глава 3 • Ветвления в Git
$ git branch -d hotfix
Deleted branch hotfix (3a0874c).

Теперь можно вернуться в ветку, где ведется работа над проблемой 53, и продолжить
ее решение (рис. 3.15):
$ git checkout iss53
Switched to branch "iss53"
$ vim index.html
$ git commit -a -m 'finished the new footer [issue 53]'
[iss53 ad82d7a] finished the new footer [issue 53]
1 file changed, 1 insertion(+)
master

C0

C1

C2

C4

C3

C5

iss53

Рис. 3.15. Продолжение работы с веткой iss53

Следует заметить, что изменения, внесенные в ветку hotfix, не касаются файлов
из ветки iss53. Ситуацию можно изменить, слив ветку master с веткой iss53, —
это делает команда git merge master. Или отложите этот процесс до момента, пока
решите включить содержимое ветки iss53 в ветку master.

Основы слияния
Предположим, работа над проблемой 53 завершена и вы готовы вставить сделанные
изменения в ветку master. Для этого следует выполнить слияние с веткой iss53
уже знакомым вам способом — как мы демонстрировали слияние с веткой hotfix.
Достаточно перейти в ветку, с которой будет осуществляться слияние, и воспользоваться командой git merge:
$ git checkout master
Switched to branch 'master'
$ git merge iss53
Merge made by the 'recursive' strategy.
README | 1 +
1 file changed, 1 insertion(+)

Основы ветвления и слияния   79

Но в данном случае наблюдаются отличия от ситуации со слиянием ветки hotfix.
Дело в том, что в некоторой точке история разработки разделилась. Коммит, соответствующий текущей ветке, не является прямым предком для ветки, с которой
осуществляется слияние, поэтому система Git должна проделать кое-какую работу.
В рассматриваемом случае Git выполнит простое трехэтапное слияние, используя
два состояния, на которые указывают вершины веток, и их общего предка (рис. 3.16).
master
Общий предок
C0

C1

C2

C4

Ветка, в которую
будут сливаться
данные

C3

C5

Ветка, данные
которой будут
слиты

iss53

Рис. 3.16. Три снимка состояния, используемые при типичном слиянии

Вместо того чтобы просто сместить указатель ветки вперед, Git формирует новое
состояние, возникающее из трехэтапного слияния, и автоматически создает коммит
для этого состояния. Его еще называют коммитом слияния (merge commit), и его
особенность состоит в наличии нескольких предков (рис. 3.17).
master

C0

C1

C2

C4

C3

C5

C5

iss53

Рис. 3.17. Коммит слияния

80    Глава 3 • Ветвления в Git
Стоить отметить, что Git определяет наиболее подходящего общего предка для
слияния веток; это отличается от более старых инструментов, таких как CVS или
Subversion (до версии 1.5), где разработчик сам должен выбирать основу для слияния. Именно это делает процедуру слияния в Git такой простой.
Теперь, когда слияние выполнено, ветка iss53 не нужна. Можно закрыть вопрос
в системе обработки заявок и удалить ветку:
$ git branch -d iss53

Конфликты при слиянии
Процесс слияния далеко не всегда проходит гладко. Если в двух ветках, которые
вы собираетесь слить, вы внесли разные изменения в один и тот же файл, Git не
сможет просто взять и объединить их. Если бы при решении проблемы 53 вы отредактировали ту же самую часть файла, что и в ветке hotfix, возник бы конфликт
слияния:
$ git merge iss53
Auto-merging index.html
CONFLICT (content): Merge conflict in index.html
Automatic merge failed; fix conflicts and then commit the result.

В этом случае Git не может автоматически создать коммит слияния. Система приостанавливает процесс до момента разрешения конфликта. Посмотреть, какие файлы
не прошли слияние после возникновения конфликта, позволяет команда git status:
$ git status
On branch master
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add ..." to mark resolution)
both modified: index.html
no changes added to commit (use "git add" and/or "git commit -a")

Все, что относится к области конфликта слияния, помечено как неслитое (unmerged).
Система Git добавляет к проблемным файлам стандартные метки, позволяющие
открывать эти файлы вручную и разрешать конфликты. Ваш файл содержит раздел,
который выглядит примерно вот так:
>iss53:index.html

Основы ветвления и слияния   81

Версия с указателем HEAD (из вашей ветки master, так как именно в нее вы пере­
шли перед выполнением команды merge ) располагается в верхней части блока (то есть выше набора символов =======), а версия из ветки iss53 показана
­в нижней части. Для разрешения конфликта следует или выбрать одну из версий,
­или каким‑то образом объединить их. К примеру, можно заменить весь блок вот
этим:

please contact us at email.support@github.com


В это решение вошло небольшое количество информации из каждой части,
а строки удалены. Разобравшись с каждым таким разделом в каждом из проблемных файлов, выполните для каждого из этих файлов
команду git add. Индексируя файл, вы помечаете его как неконфликтующий. Для
разрешения конфликтов можно вызвать командой git mergetool графический
интерфейс:
$ git mergetool
This message is displayed because 'merge.tool' is not configured.
See 'git mergetool --tool-help' or 'git help config' for more details.
'git mergetool' will now attempt to use one of the following tools:
opendiff kdiff3 tkdiff xxdiff meld tortoisemerge gvimdiff diffuse diffmerge
ecmerge p4merge
araxis bc3 codecompare vimdiff emerge
Merging:
index.html
Normal merge conflict for 'index.html':
{local}: modified file
{remote}: modified file
Hit return to start merge resolution tool (opendiff):

Если вы предпочитаете инструмент слияния, отличный от заданного по умолчанию (в данном случае система Git запустила утилиту opendiff на Mac), список
доступных инструментов выводится после строчки 'git mergetool' will now
attempt to use one of the following tools, — достаточно ввести название нужного
вам инструмента.
ПРИМЕЧАНИЕ
Более специализированные инструменты, предназначенные для разрешения запутанных
конфликтов слияния, рассмотрены чуть позже.

После завершения работы с инструментом слияния Git спросит, удалось ли разрешить конфликты. В случае положительного ответа файл индексируется, что
означает разрешение конфликта. Убедиться в том, что это действительно так, позволяет команда git status:

82    Глава 3 • Ветвления в Git
$ git status
On branch master
All conflicts fixed but you are still merging.
(use "git commit" to conclude merge)
Changes to be committed:
modified: index.html

Если вы довольны полученным результатом и удостоверились в том, что все ранее конфликтовавшие файлы проиндексированы, остается завершить слияние
­командой git commit. В этом случае сообщение фиксации по умолчанию выглядит так:
Merge branch 'iss53'
Conflicts:
index.html
#
# Кажется, вы можете зафиксировать слияние.
# Если это не так, удалите файл
#
.git/MERGE_HEAD
# и сделайте еще одну попытку.
# Введите сообщение фиксации для ваших изменений. Строки, в начале
# которых '#' игнорируются, а пустое сообщение отменяет коммит.
# On branch master
# All conflicts fixed but you are still merging.
#
# Changes to be committed:
#
modified: index.html
#

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

Управление ветками
Теперь, когда вы знаете, как создаются, сливаются и удаляются ветки, рассмотрим
другие инструменты, которые пригодятся при постоянной работе с ветками.
Команда git branch позволяет не только создавать и удалять ветки. Запущенная
без аргументов, она выводит на экран список имеющихся веток:
$ git branch
iss53
* master
testing

Приемы работы с ветками   83

Обратите внимание на символ * перед веткой master: он указывает, что именно
в этой ветке вы сейчас находитесь (то есть именно на нее нацелен указатель HEAD).
Если сейчас выполнить коммит, ветка master дополнится новыми изменениями.
Последний коммит, выполненный в каждой ветке, демонстрирует команда git
branch -v:
$ git branch -v
iss53 93b412c fix javascript issue
* master 7a98805 Merge branch 'iss53'
testing 782fd34 add scott to the author list in the readmes

Полезные параметры --merged и --no-merged оставляют в этом списке только те
ветки, которые вы слили или не слили с текущей веткой. Скажем, для просмотра
веток, объединенных с текущей веткой, следует написать git branch --merged:
$ git branch --merged
iss53
* master

В списке вы видите ветку iss53, с которой ранее было осуществлено слияние.
Ветки, перед именами которых отсутствует символ *, в общем случае удаляются
командой git branch -d; все данные оттуда уже включены в другую ветку, так что
вы ничего при этом не теряете.
Просмотр списка веток, данные которых еще не слиты в другие ветки, осуществляется командой git branch --no-merged:
$ git branch --no-merged
testing

Как видите, отображается другая ветка. Так как она содержит данные, пока не подвергшиеся слиянию, удалить ее командой git branch –d не получится:
$ git branch -d testing
error: The branch 'testing' is not fully merged.
If you are sure you want to delete it, run 'git branch -D testing'.

Если вы все равно хотите удалить эту ветку, используйте параметр -D, как указано
в подсказке.

Приемы работы с ветками
Итак, с основами ветвления и слияния вы познакомились. Возникает вопрос: что
еще можно делать с ветками? В этом разделе мы разберем некоторые стандартные рабочие приемы, ставшие возможными благодаря облегченной процедуре
ветвления. Возможно, что-то из этого вы сможете включить в собственный
цикл разработки.

84    Глава 3 • Ветвления в Git

Долгоживущие ветки
Так как в Git применяется простое трехэтапное слияние, ничто не мешает многократно объединять ветки в течение длительного времени. То есть у вас может
быть несколько постоянно открытых веток, применяемых для разных этапов
цикла разработки; содержимое некоторых из них будет регулярно сливаться
в другие ветки.
Многие разработчики, использующие Git, придерживаются именно такого подхода,
оставляя полностью стабильный код только в ветке master. При этом существует
и параллельная ветка с именем develop или next, служащая для работы и тестирования стабильности. После достижения стабильного результата ее содержимое
сливается в ветку master. Она используется для объединения завершенных задач
из тематических веток (временных веток наподобие iss53), чтобы гарантировать,
что эти задачи проходят тестирование и не вносят ошибок.
По сути, мы рассматриваем указатели, перемещающиеся по линии фиксируемых
нами изменений. Стабильные ветки находятся в нижнем конце истории коммитов,
а самые свежие наработки — ближе к ее верхней части (рис. 3.18).
master

C0

develop

C2

C3

C4

C5

topic

C6

C7

Рис. 3.18. Линейное представление повышения стабильности веток

C1

master

C2

C3

C4

C5

develop

C6

C7

Рис. 3.19. Представление диаграммы стабильности веток в виде
многоуровневого накопителя

topic

Приемы работы с ветками   85

В общем случае можно представить набор рабочих накопителей, в котором наборы коммитов перемещаются на более стабильный уровень только после полного
тестирования (рис. 3.19).
Число уровней стабильности можно увеличить. В крупных проектах зачастую появляется ветка proposed или pu (сокращение от proposed updates — предложенные
изменения), объединяющая ветки с содержимым, которое невозможно включить
в ветку next или master. Фактически каждая ветка представляет собственный
уровень стабильности; как только он повышается, содержимое сливается в ветку,
расположенную выше. Разумеется, можно и вообще обойтись без долгоживущих
веток, но зачастую они имеют смысл, особенно при работе над большими и сложными проектами.

Тематические ветки
А вот такая вещь, как тематические ветки, полезна вне зависимости от величины
проекта. Тематической (topic branch) называется временная ветка, создаваемая и
используемая для работы над конкретной функциональной возможностью или
решения сопутствующих задач. Скорее всего, при работе с другими VCS вы никогда
ничего подобного не делали, так как там создание и слияние веток — затратные
операции. Но в Git принято много раз в день создавать ветки, работать с ними,
сливать их и удалять.
Пример тематических веток вы видели в предыдущем разделе, когда мы создавали
ветки iss53 и hotfix. Для каждой из них было выполнено несколько коммитов,
после чего сразу же после слияния с основной веткой они были удалены. Такая
техника позволяет быстро и радикально осуществлять переключения контекста. Работа разделена по уровням, и все изменения в конкретной ветке относятся
к определенной теме, а значит, во время просмотра кода проще понять, что и где
было сделано. Ветку с внесенными в нее изменениями можно хранить минуты,
дни или даже месяцы, и выполнять ее слияние, только когда это действительно
требуется, независимо от порядка создания веток в рамках проекта и порядка
работы с ними.
Предположим, мы работаем в ветке master, ответвляемся для решения попутной
проблемы (iss91), некоторое время занимаемся ею, затем создаем ветку, чтобы
попробовать решить эту задачу другим способом (iss91v2), возвращаемся в ветку
master, выполняем там некие действия и создаем новую ветку для действий, в результате которых не уверены (ветка dumbidea). Результирующая история коммитов
будет выглядеть примерно так, как показано на рис. 3.20.
Предположим, вам больше нравится второй вариант решения задачи (iss91v2),
а ветку dumbidea вы показали коллегам, и оказалось, что там содержится ге­ниальная
идея. Фактически вы можете удалить ветку iss91 (потеряв коммиты C5 и C6) и слить
две другие ветки. После этого история будет соответствовать рис. 3.21.

86    Глава 3 • Ветвления в Git
dumbidea
C13

C12

master

iss91

iss91v2

C10

C6

C11

C9

C5

C8

C3

C4

C7

C1

C2

C0

Рис. 3.20. Набор тематических веток

master
C14

dumbidea

C13

C11

C12

C8

C10

C7

C9

C4

C3

C2

iss91v2

C1

C0

Рис. 3.21. История после слияния веток dumbidea и iss91v2

Удаленные ветки   87

Более подробно допустимые варианты рабочих схем для проектов рассматриваются
в главе 5, поэтому перед выбором схемы обязательно прочитайте эту главу.
Важно помнить, что во время всех этих манипуляций ветки полностью локальны.
Ветвления и слияния выполняются только в репозитории Git, связь с сервером не
требуется.

Удаленные ветки
Удаленные ветки (remote branches) представляют собой ссылки на состояния веток
в удаленных репозиториях. Перемещать их локально вы не можете; они смещаются
автоматически при каждом подключении по сети. Удаленные ветки работают как
закладки, напоминающие, где в удаленных репозиториях находились соответствующие ветки во время вашего последнего подключения к ним.
Они имеют форму (имя удаленного репозитория)/(ветка). К примеру, чтобы
увидеть, как выглядела ветка master на сервере origin во время последнего взаимодействия с ним, проверьте ветку origin/master. Если вы выполняли совместную
работу с коллегой и он отправил на сервер ветку iss53, у вас может присутствовать
собственная локальная копия этой ветки; но ветка на сервере будет указывать на
коммит по адресу origin/iss53.
Проясним ситуацию на примере. Предположим, у вас есть Git-сервер с адресом git.
ourcompany.com. При клонировании с этого адреса Git автоматически присваивает
копии имя origin, извлекает все данные, создает указатель на местоположение
ветки master и присваивает ему локальное имя origin/master. Одновременно
вам предоставляется собственная локальная ветка master, берущая начало в том
же самом месте, что и одноименная ветка копии, что дает вам отправную точку для
начала работы (рис. 3.22).
ИМЯ ORIGIN
Имя ветки «origin», как и имя «master», в Git не несет особого значения. Имя master
широко распространено потому, что оно по умолчанию присваивается ветке, порождаемой командой git init, а имя origin по умолчанию присваивается удаленной
ветке, порождаемой командой git clone. Если написать git clone -o booyah, по
умолчанию вы будете работать с удаленной веткой booyah/master.

Если во время вашей работы на локальной ветке master кто-то отправит на сервер
git.ourcompany.com результаты своего труда и обновит удаленную ветку master, истории веток будут развиваться по-разному (рис. 3.23). Кроме того, до момента связи
с сервером origin указатель origin/master у вас двигаться не будет.

88    Глава 3 • Ветвления в Git
git.ourcompany.com
master

0b743

a6b4c

f4265

git clone janedoe@git.ourcompany.com:project.git
Компьютер

0b743

a6b4c

origin/master

Удаленная
ветка

f4265

master

Локальная
ветка

Рис. 3.22. Удаленный и локальный репозитории после клонирования

git.ourcompany.com

0b743

a6b4c

f4265

master

31b8e

190a3

Кто-то отправляет данные
Компьютер
origin/master

0b743

a6b4c

f4265

a38de

893cf

master

Рис. 3.23. Локальная и удаленная версии ветки могут различаться

Удаленные ветки   89

Синхронизация работы осуществляется командой git fetch origin. Она ищет
сервер с именем «origin» (в данном случае это сервер git.ourcompany.com), извлекает
оттуда все пока отсутствующие у вас данные, обновляет вашу локальную базу
данных и сдвигает указатель origin/master на новую, более актуальную позицию
(рис. 3.24).
git.ourcompany.com
master

0b743

a6b4c

f4265

31b8e

190a3

Кто-то отправляет данные
git fetch origin
Компьютер

0b743

a6b4c

f4265

origin/master

31b8e

190a3

a38de

893cf

master

Рис. 3.24. Команда git fetch обновляет ссылки на удаленный репозиторий

Чтобы продемонстрировать, как будут выглядеть удаленные ветки при наличии
нескольких удаленных серверов, предположим, что у вас есть еще один внутренний
сервер Git, которым пользуется только одна из групп разработчиков. Этот сервер
находится по адресу git.team1.ourcompany.com. Добавить его в качестве новой удаленной ссылки на проект, над которым вы сейчас работаете, можно уже знакомой
вам командой git remote add (рис. 3.25). Присвойте этому удаленному серверу
имя teamone.
Теперь можно воспользоваться командой git fetch teamone и получить все данные,
отсутствующие у вас, но имеющиеся на сервере teamone. Однако поскольку пока
этот сервер содержит всего лишь часть данных с сервера origin, система Git ничего
не скачает, а только сгенерирует удаленную ветку teamone/master, указывающую
на тот же коммит, что и ветка master на сервере teamone (рис. 3.26).

90    Глава 3 • Ветвления в Git
git.ourcompany.com

git.team1.ourcompany.com
master

f4265

31b8e

master

f4265

190a3

origin

31b8e
teamone

git remote add teamone git://git.team1.ourcompany.com
Компьютер

0b743

a6b4c

f4265

origin/master

31b8e

190a3

a38de

893cf

master

Рис. 3.25. Добавление еще одного удаленного сервера
git.ourcompany.com

git.team1.ourcompany.com
master

f4265

31b8e
origin

master

f4265

190a3
git fetch teamone
Компьютер

0b743

a6b4c

f4265

31b8e
teamone

origin/master

origin/master

31b8e

190a3

a38de

893cf

master

Рис. 3.26. Удаленная отслеживающая ветка для ветки teamone/master

Удаленные ветки   91

Отправка данных
Чтобы поделиться содержимым своей ветки с окружающими, ее нужно отправить
на удаленный сервер, на котором у вас есть права на запись. Автоматической синхронизации локальных веток с удаленными серверами не происходит, поэтому
ветки, которые вы хотите выставить на всеобщее обозрение, следует отправлять
вручную. Такой подход дает возможность работать с личными ветками независимо
от коллег, предоставляя доступ только к тематическим веткам, предназначенным
для совместной работы.
Предположим, у вас есть ветка serverfix, над которой вы хотите работать вместе
с коллегами. Ее следует отправить на сервер тем же способом, каким была отправлена ваша первая ветка, то есть командой git push (удаленный сервер) (ветка):
$ git push origin serverfix
Counting objects: 24, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (15/15), done.
Writing objects: 100% (24/24), 1.91 KiB | 0 bytes/s, done.
Total 24 (delta 2), reused 0 (delta 0)
To https://github.com/schacon/simplegit
* [new branch] serverfix -> serverfix

Это в некотором роде сокращение. Система Git автоматически превращает имя ветки
serverfix в запись refs/heads/serverfix:refs/heads/serverfix, что озна­чает
«возьмите мою локальную ветку serverfix и используйте для обновления удаленной ветки serverfix». Фрагмент refs/heads/ мы будем обсуждать в главе 10, но, как
правило, его можно просто опустить. Вы также можете воспользоваться ­командой
git push origin serverfix:serverfix, которая делает то же самое. Кроме того,
данный формат позволяет переслать содержимое локальной ветки в удаленную
ветку с другим именем. Достаточно вместо имени serverfix на удаленном сервере указать другой вариант, например git push origin serverfix:awesomebranch.
В этом случае содержимое локальной ветки serverfix будет передано в ветку
awesomebranch на удаленном сервере.
ПРИМЕЧАНИЕ
Каждый раз вводить свой пароль не нужно. Если для отправки данных вы используете
протокол HTTPS, сервер Git будет просить вас указать имя пользователя и пароль для
проверки прав доступа. По умолчанию эту информацию предлагается ввести в терминале, чтобы сервер мог сообщить вам, имеете ли вы право отправлять ему данные.
Чтобы не делать этого при каждой отправке, настройте «кэш учетных данных». Проще
всего несколько минут держать данную информацию в памяти, что легко достигается
командой git config --global credential.helper cache.

Когда кто-то из ваших коллег в следующий раз решит скачать обновления с сервера,
он получит ссылку на место, куда серверная версия ветки serverfix указывает как
на удаленную ветку origin/serverfix:

92    Глава 3 • Ветвления в Git
$ git fetch origin
remote: Counting objects: 7, done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 3 (delta 0)
Unpacking objects: 100% (3/3), done.
From https://github.com/schacon/simplegit
* [new branch] serverfix -> origin/serverfix

Имейте в виду, что получение новых удаленных веток при скачивании данных не
означает автоматического появления их доступных для редактирования копий.
Другими словами, у вас появляется не новая ветка serverfix, а только недоступный
для редактирования указатель origin/serverfix.
Тем не менее данные оттуда можно слить в текущую рабочую ветку, воспользовавшись командой git merge origin/serverfix. Если вам требуется собственная
копия ветки serverfix, достаточно создать ее, взяв за основу удаленную ветку:
$ git checkout -b serverfix origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

В результате вы получаете для работы локальную ветку, которая начинается там
же, где и ветка origin/serverfix.

Слежение за ветками
При переходе в локальную ветку, созданную из удаленной, автоматически появляется так называемая ветка наблюдения (tracking branch). Это локальная ветка,
напрямую связанная с удаленной. Если, находясь на этой ветке, вы наберете
команду git push, система Git автоматически поймет, на какой сервер и в какую
ветку нужно отправлять данные. А команда git pull в этом случае скачивает все
удаленные ссылки и после этого автоматически выполняет слияние в соответствующую удаленную ветку.
При клонировании репозитория автоматически создается ветка master, следящая
за веткой origin/master. Именно поэтому команды git push и git pull выполняются из этой ветки без аргументов. При желании можно создавать и другие
ветки наблюдения, следящие за ветками на других удаленных серверах или не отслеживающие происходящее на ветке master. Простой пример реализации этого
сценария вы только что видели — команда git checkout -b [ветка] [имя удаленного
сервера]/[ветка]. Это достаточно распространенная операция, для которой в Git
существует параметр --track:
$ git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to a new branch 'serverfix'

Если вы хотите, чтобы имя локальной ветки отличалось от имени удаленной ветки,
используйте первую версию команды с указанием нового имени:

Удаленные ветки   93
$ git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switched to a new branch 'sf'

Теперь локальная ветка sf поддерживает автоматический обмен данными с удаленной веткой origin/serverfix.
Если вы хотите сопоставить только что скачанной удаленной ветке существующую
локальную ветку или поменять удаленную ветку, за которой вы следите, используйте
параметр -u или --set-upstream-to команды git branch.
$ git branch -u origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.

СОКРАЩЕННОЕ ОБОЗНАЧЕНИЕ
Для обращения к существующей ветке наблюдения есть сокращенные формы­­
@{upstream} или @{u}. К примеру, если из ветки master вы следите за веткой
origin/master, для краткости можно писать git merge @{u} вместо git merge
origin/master.

Получить список веток наблюдения позволяет параметр -vv команды git branch.
В результате выводится перечень локальных веток с дополнительной информацией, касающейся того, за чем следит каждая ветка и на сколько она опережает
соответствующую ветку на удаленном сервере или отстает от нее:
$ git branch -vv
iss53
7e424c3
master
1ae2a45
* serverfix f8674d9
testing
5ea463a

[origin/iss53: ahead 2] forgot the brackets
[origin/master] deploying index fix
[teamone/server-fix-good: ahead 3, behind 1] this should do it
trying something new

Здесь мы видим, что ветка iss53 связана с веткой origin/iss53 и при этом запись
«ahead 2» означает наличие двух локальных коммитов, пока не отправленных на
сервер. Наша ветка master связана с веткой origin/master и содержит актуальную информацию. А вот ветка serverfix, связанная с веткой server-fix-good на
сервере teamone, помечена как «ahead 3, behind 1», то есть на сервере существует
коммит, который в нее пока не слит, а на ней присутствуют три коммита, пока не
отправленные на сервер. Наконец, мы видим, что ветка testing не связана ни
с одной удаленной веткой.
Отметим, что все цифры представляют собой показатели, зафиксированные
­в момент последнего скачивания данных с каждого сервера. Данная команда не
­обращается к серверам, а просто сообщает локальные данные из кэша. Для получения актуальной информации о количестве новых коммитов на локальных и
удаленных ветках следует извлечь данные со всех удаленных серверов и только
затем воспользоваться этой командой. Это можно сделать так: $ git fetch --all;
git branch -vv.

94    Глава 3 • Ветвления в Git

Получение данных с последующим слиянием
Хотя команда git fetch забирает с сервера всю пока отсутствующую у вас новую
информацию, на состояние рабочей папки она никак не влияет. Она всего лишь
предоставляет данные, которые можно подвергнуть слиянию. Но существует и
команда git pull, по сути, представляющая собой команду git fetch, за которой
немедленно следует команда git merge. При наличии ветки наблюдения, настроенной, как показано в предыдущем разделе, вручную или в момент ее генерации
командой clone или checkout, команда git pull будет обращаться к серверу и
ветке, за которой наблюдает ваша текущая ветка, скачивать данные с этого сервера
и пытаться слить их с этой удаленной веткой.
В общем случае лучше отдельно пользоваться командами fetch и merge, в явном
виде указывая подлежащую слиянию информацию, так как автоматизм команды
git pull может привести к путанице.

Ликвидация веток с удаленного сервера
Предположим, работа с удаленной веткой закончилась, и программный компонент,
над которым вы с коллегами трудились, слит в удаленную ветку master (или в ту
ветку, где вы храните стабильный вариант кода). Теперь можно избавиться от удаленной ветки, добавив команде git push параметр --delete. А вот как выглядит
удаление ветки serverfix с сервера:
$ git push origin --delete serverfix
To https://github.com/schacon/simplegit
- [deleted] serverfix

По сути, эта команда просто удаляет с сервера указатель. Как правило, Git-сервер
хранит данные, пока в дело не вступит сборщик мусора, поэтому, в случае ошибочного удаления, информацию зачастую легко восстановить.

Перемещение данных
В Git есть два основных способа включения изменений из одной ветки в другую:
слияние (merge) и перемещение (rebase). В этом разделе мы рассмотрим процесс
перемещения, детали его реализации, достоинства и случаи, когда его стоит и не
стоит использовать.

Основы перемещения данных
Вспомним пример из раздела, посвященного слиянию. Мы разделили работу на
две части и фиксировали изменения в двух разных ветках (рис. 3.27).

Перемещение данных   95

experiment

C4

C0

C1

C2

C3

master

Рис. 3.27. Простой случай разделения истории

Простейшим средством объединения этих веток, как мы уже выяснили, является
команда merge (рис. 3.28). Она в три шага выполняет слияние последних снимков
состояния из разных веток (C3 и C4) и последнего общего предка этих состояний
(C2), генерируя при этом новый снимок состояния (и коммит).
experiment

C4

C0

C1

C2

C3

C5

master

Рис. 3.28. Слияние для объединения разделившихся историй разработки

Но существует и другой способ: можно взять фрагмент изменений, появившийся
в коммите C4, и применить его поверх коммита C3. В Git эта операция называется
перемещением (rebasing). Команда rebase позволяет взять изменения, зафиксированные в одной ветке, и повторить их в другой.
В рассматриваемом примере это будет выглядеть так:
$ git checkout experiment
$ git rebase master
First, rewinding head to replay your work on top of it...
Applying: added staged command

Это работает следующим образом: ищется общий предок двух веток (текущей ветки
и ветки, в которую вы выполняете перемещение), вычисляется разница, вносимая

96    Глава 3 • Ветвления в Git
каждым коммитом текущей ветки, и сохраняется во временных файлах. После этого
текущая ветка сопоставляется тому же коммиту, что и ветка, в которую осуществ­
ляется перемещение, и одно за другим происходят все изменения (рис. 3.29).

C4

C0

C1

C2

C3

experiment

C4'

master

Рис. 3.29. Перемещение изменений, внесенных в коммите C3, в коммит C4

На этом этапе можно вернуться в ветку master и выполнить слияние перемотки
(рис. 3.30).
experiment

C0

C1

C2

C3

C4'

master

Рис. 3.30. Перемотка ветки master

Теперь снимок состояния, на который указывает коммит C3, полностью совпадает
со снимком состояния, на который указывал коммит C5 в примере со слиянием.
Разницы в конечном результате слияния нет, но в случае перемещения мы получаем более аккуратную историю. В журнале регистрации перемещенной ветки вы
увидите линейное развитие событий. Все будет выглядеть так, как будто работа
выполнялась последовательно, в то время как она велась параллельно.
Часто это делается, чтобы убедиться в четкости фиксируемых вами состояний на
удаленной ветке, например в случае чужого проекта, в который вы хотите внести
вклад. В такой ситуации оптимально работать в отдельной ветке, а затем, когда вы
почувствуете, что готовы добавить свои исправления к основной версии проекта,
переместить данные в ветку origin/master. Это избавит владельца проекта от необходимости заниматься интеграцией — достаточно прибегнуть к перемотке или
просто применить добавленные изменения.
Обратите внимание, что в обоих случаях — последнего перемещенного коммита
и итогового коммита слияния — коммиты указывают на один и тот же снимок

Перемещение данных   97

состояния, разной оказывается только история. При перемещении изменения
из одной линии разработки применяются к другой в порядке их появления, в то
время как при слиянии берутся конечные точки двух веток и соединяются друг
с другом.

Более интересные варианты перемещений
Можно сделать так, чтобы воспроизведение результатов перемещения начиналось
не с ветки, куда они были перемещены. В качестве примера рассмотрим историю
разработки с рис. 3.31. Для добавления к проекту функциональности на стороне
сервера была создана тематическая ветка (server), состояние которой в какой-то
момент было зафиксировано. После этого была создана еще одна ветка (client)
для внесения изменений в клиентскую часть, и в ней несколько раз выполнялась
фиксация состояний. Наконец, произошло возвращение в ветку server, где также
создано несколько коммитов.
master

C1

C2

C5

C6

C3

C4

C10

server

C8

C9

client

Рис. 3.31. История с тематической веткой, отходящей от другой
тематической ветки

Предположим, вы хотите внести изменения клиентской части в окончательную
версию кода, оставив изменения серверной части для дальнейшего тестирования.
Взять изменения клиентской части (коммиты C8 и C9), не связанные с изменениями
на серверной стороне, и воспроизвести их в ветке master позволяет команда git
rebase с параметром --onto:
$ git rebase --onto master server client

98    Глава 3 • Ветвления в Git
По сути, она приказывает «перейти в ветку client, найти исправления от общего
предка веток client и server и повторить их в ветке master». Выглядит сложно,
но дает интересный результат (рис. 3.32).
master

C1

C2

C5

C6

C8'

C9'

C3

C4

C10

client

server

C8

C9

Рис. 3.32. Перемещение тематической ветки, начатой от другой тематической ветки

Теперь можно выполнить перемотку ветки master (рис. 3.33):
$ git checkout master
$ git merge client

master

C1

C2

C5

C6

C8'

C9'

C3

C4

C10

client

server

Рис. 3.33. Слияние перемотки ветки master для добавления изменений из ветки client

Предположим, вы решили добавить в основной код также работу из ветки
server. Переместить эту ветку в ветку master, вне зависимости от того, в какой
ветке ­вы сейчас находитесь, позволяет команда git rebase [основная ветка]

Перемещение данных   99

[тематическая ветка]. Она переключает вас на тематическую ветку (в данном
­случае — на ветку server) и воспроизводит ее содержимое в основной ветке (master):
$ git rebase master server

Это добавляет наработки из ветки server к наработкам в ветке master (рис. 3.34).
master

C1

C2

C5

C6

C8'

C9'

C3

C4

C10

client

C3'

C4'

C10'

server

Рис. 3.34. Перемещение ветки server в конец ветки master

После этого можно выполнить перемотку основной ветки (master):
$ git checkout master
$ git merge server

Ветки client и server теперь можно удалить, так как все их содержимое встроено
в основной код и они больше не нужны (рис. 3.35).
$ git branch -d client
$ git branch -d server
master

C1

C2

C5

C6

C8'

C9'

C3'

C4'

C10'

Рис. 3.35. Окончательный вид истории коммитов

Риски, связанные с перемещением
К сожалению, такая удобная операция, как перемещение, не лишена подводных
камней. Следует знать о недопустимости перемещения коммитов, существующих
вне вашего репозитория.
Пока вы следуете этому правилу, все хорошо. Нарушьте его и на вас обрушится
всеобщее осуждение.
В процессе перемещения вы прекращаете работу с существующими коммитами и
создаете новые, которые в целом аналогичны старым, но кое в чем отличаются. Если
вы выкладываете зафиксированные результаты своего труда в публичный доступ,

100    Глава 3 • Ветвления в Git
а кто-то скачивает их и использует как основу для своей работы, при переписывании
коммитов командой git rebase и повторной отправке их на сервер вашим коллегам
придется заново выполнять слияние. В результате при попытке инкорпорировать
их работу в свою возникнет путаница.
Продемонстрируем, каким образом перемещение выложенных в общий доступ
наработок становится источником проблем. Предположим, вы клонировали
­репозиторий с центрального сервера и выполнили с ним некие действия (рис. 3.36).
Кто-то из ваших коллег также делает работу, производит слияние и отправляет результат на центральный сервер. Вы извлекаете эти данные и присоединяете к своей
работе новую удаленную ветку (рис. 3.37).
git.team1.ourcompany.com
C1

master

Компьютер
C1

teamone/master

C2

C3

master

Рис. 3.36. Клонирование репозитория и выполнение на его основе
какой-то работы
git.team1.ourcompany.com
C5

C1

C4

C6

master

Компьютер
C5

C1

C4

C6

C2

C3

teamone/master

C7

master

Рис. 3.37. Извлечение дополнительных коммитов и добавление
их к своей работе

Перемещение данных   101

Но человек, выложивший подвергшийся слиянию коммит, решает все переиграть и
выполнить вместо слияния перемещение; он использует команду git push –force,
чтобы переписать историю на сервере. А после этого вы скачиваете с этого сервера
изменения, включающие новые коммиты (рис. 3.38).
В результате вы оба попадаете в затруднительное положение. Команда git pull создаст у вас слитый коммит, включающий оба варианта развития истории (рис. 3.39).
git.team1.ourcompany.com

C1

C5

C4'

C4

C6

master

Компьютер

C1

C5

C4'

C4

C6

C2

C3

teamone/master

C7

master

Рис. 3.38. Кто-то выложил перемещенные коммиты, убрав коммиты,
на которых базировалась ваша работа
git.team1.ourcompany.com

C1

C5

C4'

C4

C6

master

Компьютер

C1

C5

C4'

C4

C6

C2

C3

teamone/master
master

C7

C8

Рис. 3.39. Вы снова добавляете те же самые наработки в новый коммит слияния

102    Глава 3 • Ветвления в Git
Команда git log в случае подобной истории показывает два коммита одного
и того же автора с одинаковой датой и одним сообщением слияния, что приводит
к путанице. Более того, отправка этой истории обратно в общий доступ добавляет
перемещенные коммиты в репозиторий центрального сервера, что еще сильнее
­запутывает пользователей. В подобном случае разумнее предположить, что другой
разработчик не хотел видеть в истории коммиты C4 и C6; именно по этой причине
он изначально прибег к перемещению.

Перемещение после перемещения
Для оказавшихся в подобной ситуации у Git есть палочка-выручалочка. Если кто-то
из вашей рабочей группы волевым решением вносит изменения, меняющие данные,
на которых вы базируете свою работу, первым делом следует определить, где ваши
данные, а где данные, подвергшиеся перезаписи.
Оказывается, кроме контрольной суммы SHA коммита система Git вычисляет
контрольную сумму, базирующуюся на внесенном вместе с коммитом исправлении.
Это называется идентификатором исправления (patch-id).
В случае скачивания и добавления к вашей работе переопределенных данных с последующим перемещением их на новые коммиты от партнера Git зачастую успешно
выделяет ваши уникальные данные и накладывает их на верхнюю часть новой ветки.
Если бы в ситуации с рис. 3.38 мы прибегли не к слиянию брошенных коммитов,
служащих основой нашей работы, а воспользовались командой git rebase teamone/
master, действия Git были бы следующими:
1. Определить работу, уникальную для нашей ветки (C2, C3, C4, C6, C7).
2. Определить, что не является коммитами слияния (C2, C3, C4).
3. Определить, что не нужно переписывать в целевую ветку (только C2 и C3, так
как C4 — это то же самое исправление, что и C4').
4. Применить эти коммиты на ветке teamone/master.
И вместо картинки, показанной на рис. 3.39, мы получили бы результат, больше
напоминающий рис. 3.40.
Это работает только в случае, когда коммиты C4 и C4' от вашего партнера содержат
практически одинаковые исправления. В противном случае процедура перемещения не распознает их как дубликаты и добавит еще одно C4-образное исправление
(которое, скорее всего, не удастся без проблем применить, так как эти изменения,
по крайней мере до какой-то степени, уже присутствуют).
Упростить ситуацию можно, применив команду git pull --rebase вместо обычной
команды git pull. Все то же самое можно также сделать вручную командой git
fetch, за которой в данном случае следует команда git rebase teamone/master.

Перемещение данных   103

git.team1.ourcompany.com

C1

C5

C4'

C4

C6

master

Компьютер
master

teamone/master

C1

C5

C4'

C2'

C3'

Рис. 3.40. Перемещение после перемещений, приведших к путанице

Если вы хотите, чтобы команда git pull по умолчанию запускалась с параметром
--rebase, значению pull.rebase config присваивается нечто вроде git config
--global pull.rebase true.
Если вы рассматриваете перемещение как операцию для наведения порядка и для
работы с коммитами, которые пока не отправлены в общий доступ, все будет в
порядке. Перемещение же коммитов, которые уже оказались в общем доступе
и могли послужить основой для работы ваших коллег, может стать источником
массы проблем.
В ситуации, когда вы или кто-то из ваших коллег все-таки считает необходимым
перемещение коммитов, находящихся в общем доступе, доведите до сведения всех
заинтересованных лиц, что им нужно воспользоваться командой git pull --rebase,
чтобы хоть немного смягчить последствия данной операции.

Сравнение перемещения и слияния
Теперь, когда мы рассмотрели несколько процедур перемещения и слияния, может
возникнуть вопрос, какая же из операций лучше. Перед тем как ответить на него,
вернемся немного назад и поговорим о том, какой смысл несет история.
Существует точка зрения, что история коммитов вашего репозитория — это хроника работы с системой. Это документ, ценный сам по себе, и его подделка недопустима. Соответственно вмешательство в историю коммитов представляет собой

104    Глава 3 • Ветвления в Git
почти кощунство — вы фальсифицируете то, что происходило. Что делать в случае
запутанной истории коммитов слияния? Ничего. Таким способом велась работа,
и репозиторий должен сохранить эти подробности для будущих поколений.
Но есть и другая точка зрения. История коммитов — это то, что дает представление
о структуре вашего проекта. Вы же не публикуете первый черновик книги, и даже
руководство по поддержке выпускаемого вами программного обеспечения подвергается тщательному редактированию. Представители этого лагеря пользуются
такими инструментами, как перемещение и фильтрация веток, чтобы представить
историю в удобном для будущих читателей виде.
Наверное, вы уже сами понимаете, что на вопрос о превосходстве одной операции над
другой однозначного ответа не существует. Система Git является мощным инструментом для реализации множества вещей, как строящих историю, так и меняющих
ее, но в конечном счете все зависит от конкретной рабочей группы и конкретного
проекта. Теперь, когда вы знакомы с деталями функционирования каждой команды,
вы можете самостоятельно выбрать оптимальный в вашей ситуации инструмент.
В общем случае самый лучший результат обеспечивает перемещение локальных
изменений, которые вы внесли, но пока не отправили в общий доступ, с целью приведения истории в порядок. Главное — никогда не перемещать данные, оказавшиеся
запределами вашего локального репозитория.

Заключение
Итак, мы рассмотрели основы ветвления и слияния в Git. Вы должны уверенно
создавать новые ветки и переходить на них, без проблем выполнять переходы между
ветками и слияние локальных веток. Кроме того, вы должны уметь выкладывать
свои ветки в общий доступ, работать вместе с коллегами на таких ветках и выполнять перемещения своих веток до отправки их содержимого на всеобщее обозрение. В следующей главе мы поговорим о деталях поддержки вашего собственного
сервера с Git-репозиторием.

4

Git на сервере

К этому моменту вы должны уметь решать большую часть рутинных задач, возникающих при работе с Git. Но для совместной работы требуется удаленный репозиторий. Разумеется, ничто не мешает вам добавлять изменения в чужие личные
репозитории и скачивать оттуда обновления, но такая практика не приветствуется,
так как легко приводит к путанице. К тому же вряд ли вам хотелось бы, чтобы у
кого-то постороннего был доступ к вашему репозиторию и тогда, когда ваш компьютер выключен, а значит, требуется более надежный общественный репозиторий.
Соответственно, для совместной работы лучше всего организовать промежуточный
репозиторий, доступ на запись и на скачивание информации из которого будет
у всех заинтересованных лиц.
Запуск сервера Git реализуется достаточно просто. Первым делом выбирается протокол для связи с этим сервером. Первая часть данной главы посвящена описанию
доступных протоколов с их достоинствами и недостатками. Затем мы обсудим
типичные конфигурации с применением этих протоколов и настройку сервера для
работы с ними. Напоследок мы рассмотрим варианты хостинга, подходящие тем,
кто готов разместить свой код на чужом сервере, так как не хочет возиться с настройкой и поддержанием собственного.
Если настройка собственного сервера вас не интересует, сразу переходите к концу
главы, где обсуждаются варианты настройки учетных записей для существующего
хостинга, а затем к следующей главе, посвященной тонкостям работы с распределенной системой контроля версий.

106    Глава 4 • Git на сервере
Удаленный репозиторий, как правило, представляет собой репозиторий без рабочей
папки. Так как он предназначен исключительно для обмена данными, создавать
рабочую копию на диске попросту незачем. Проще говоря, такой репозиторий содержит только каталог .git проекта и больше ничего.

Протоколы
Для передачи данных система Git пользуется четырьмя основными протоколами: локальным, HTTP, SSH (Secure Shell) и Git. В этом разделе мы поговорим об
их особенностях и о том, в каких обстоятельствах имеет смысл применять каждый
из них.

Локальный протокол
Основным является так называемый локальный протокол (local protocol), используемый в случаях, когда роль удаленного репозитория играет папка на этом же
диске. Чаще всего он используется при наличии у всех членов группы доступа к
общей файловой системе, например к NFS, или в менее распространенном случае,
когда все авторизуются на одном компьютере. Второй вариант не рекомендуется,
так как нахождение всех экземпляров репозитория на одной машине повышает
риск катастрофической потери результатов.
Смонтированная общая файловая система дает возможность клонировать локальный репозиторий и обмениваться с ним данными. Чтобы осуществить клонирование
или добавить такой репозиторий в качестве удаленного в существующий проект,
следует указать путь к нему в виде адреса URL. К примеру, вот как выглядит клонирование локального репозитория:
$ git clone /opt/git/project.git

Допустим и такой вариант команды:
$ git clone file:///opt/git/project.git

Наличие приставки file:// в начале URL-адреса несколько меняет поведение Git.
Если указан только путь, Git пытается использовать жесткие ссылки или непосредственно копировать необходимые файлы. А приставка file:// заставляет
Git запустить процессы, обычно используемые для передачи данных по сети, что
в целом менее эффективно. Но в этом случае вы получаете полную копию репозитория с дополнительными ссылками и оставшимися объектами — обычно после
импорта из другой системы контроля версий (см. информацию об инструментах
обслуживания в главе 10). Мы будем пользоваться стандартными путями, так как
практически всегда это более быстрый способ.

Протоколы   107

Для добавления в существующий проект локального репозитория применяется
следующая команда:
$ git remote add local_proj /opt/git/project.git

Теперь вы можете отправлять в этот репозиторий данные об изменениях и скачивать
оттуда обновления точно так же, как это делалось по сети.

Преимущества
К преимуществам файлового репозитория относится его простота и возможность использовать существующие полномочия на доступ к файлам и сетевой
доступ. При наличии доступной всей группе файловой системы настроить
репозиторий очень легко. Достаточно поместить его основу в общедоступное
место и указать права на чтение/запись, как в случае с любой совместно используемой папкой. Экспорт голой копии репозитория мы обсудим в разделе
«Настройка Git на сервере».
Кроме того, это хорошая возможность быстро забрать данные из чужого рабочего
репозитория. Если вы с коллегой работаете над одним проектом и он просит что-то
проверить, применить команду git pull /home/john/project зачастую намного
проще и быстрее, чем получать с удаленного сервера данные, которые он предварительно туда отправил.

Недостатки
Недостаток этого метода состоит в том, что общий доступ из разных мест настроить и получить сложнее, чем обычный сетевой доступ. К примеру, для
отправки данных в репозиторий со своего домашнего компьютера вам нужно
будет смонтировать удаленный диск, а это более медленная и сложная процедура, чем доступ по сети.
Также следует упомянуть, что использование папок общего доступа далеко не
всегда является самым быстрым способом работы. Локальный репозиторий
функционирует быстро только при наличии быстрого доступа к данным. Доступ
к репозиторию в системе NFS часто медленнее доступа по протоколу SSH на
том же самом сервере, при котором Git может работать с локальными дисками
на каждой системе.

Протоколы семейства HTTP
Взаимодействия Git по протоколу HTTP осуществляются в двух режимах. До версии
Git 1.6.6 существовал всего один способ, очень простой и в общем случае дающий
доступ только на чтение. В версии 1.6.6 появился новый, более интеллектуальный
протокол, позволяющий системе Git реализовать передачу данных примерно таким

108    Глава 4 • Git на сервере
же образом, как это происходит по протоколу SSH. В последние годы популярность
этого нового протокола HTTP крайне возросла, так как пользователям проще с ним
работать, а взаимодействия организованы намного аккуратнее. Более новая версия
часто называется «интеллектуальным» протоколом HTTP, в то время как более
старую называют «простым» протоколом HTTP. Первым делом мы рассмотрим
«интеллектуальную» версию.

Интеллектуальный протокол HTTP
Интеллектуальный протокол HTTP функционирует во многом аналогично протоколу SSH или Git, но передает данные через стандартные порты HTTP/S и пользуется различными механизмами HTTP-аутентификации. То есть пользователям
проще работать с ним, чем, например, с SSH, так как возможна аутентификация
по имени пользователя/паролю, позволяющая избежать настройки ключей SSH.
В настоящее время это, пожалуй, самый популярный способ работы с Git, так как
наряду с анонимным обслуживанием, как в случае протокола git://, он допускает
аутентификацию и шифрование, аналогично протоколу SSH. И вместо набора URLадресов для этого можно обойтись всего одним адресом. Если попытка отправить
данные в репозиторий сопровождается требованием аутентификации (что, как
правило, является нормой), сервер предлагает указать имя пользователя и пароль.
То же самое происходит при получении доступа на чтение.
По сути, для таких служб, как GitHub, URL-адрес, используемый для просмотра
репозитория в сети (например, https://github.com/schacon/simplegit[]), может применяться также и для клонирования при наличии у вас прав на запись.

Простой протокол HTTP
Если сервер не реагирует на интеллектуальный вариант HTTP, Git-клиент пользуется предшествующей, более простой версией протокола HTTP. С точки зрения
этого протокола голый Git-репозиторий обслуживается как обычные файлы с вебсервера. Прелесть простого протокола HTTP состоит в простоте его настройки. По
сути, достаточно поместить голый Git-репозиторий в корневую папку с HTTPдокументами и настроить хук, срабатывающий после обновления (post-update
hook). После этого клонировать репозиторий сможет любой, кто имеет доступ
к веб-серверу, где этот репозиторий расположен. Предоставить доступ на чтение
по протоколу HTTP можно, например, следующим образом:
$
$
$
$
$

cd /var/www/htdocs/
git clone --bare /path/to/git_project gitproject.git
cd gitproject.git
mv hooks/post-update.sample hooks/post-update
chmod a+x hooks/post-update

Вот и все. Срабатывающий после обновления хук, по умолчанию устанавливаемый с Git, будет запускать команду (git update-server-info), обеспечивающую

Протоколы   109

корректное скачивание информации и клонирование по протоколу HTTP. Эта
команда выполняется после каждой отправки изменений в репозиторий (скажем,
по протоколу SSH); после этого другие пользователи могут выполнить клонирование командой:
$ git clone https://example.com/gitproject.git

В этом примере фигурировал путь /var/www/htdocs, общепринятый при настройке
серверов Apache, но вы можете воспользоваться любым статическим веб-сервером —
достаточно поместить в нужную папку голый репозиторий. Данные Git в таком
случае обслуживаются как обычные статические файлы (подробно эта процедура
рассматривается в главе 10).
В общем случае вы можете либо запустить сервер с доступом на чтение/запись по
интеллектуальному протоколу HTTP, либо предоставить доступ к файлам только
на чтение через обычный протокол. Вместе эти два варианта протокола практически
никогда не используются.

Достоинства
Достоинства, о которых мы упомянем, относятся к интеллектуальной версии
протокола. Наличие единого URL-адреса для всех типов доступа и приглашение
на ввод личных данных, только когда это требуется для аутентификации, упрощают работу конечного пользователя. Аутентификация по имени пользователя
и паролю также является огромным преимуществом перед протоколом SSH,
ведь пользователю не приходится локально генерировать SSH-ключи и отправлять свои открытые ключи на сервер, чтобы получить возможность с ним
взаимодействовать. Для менее опытных пользователей и пользователей систем, в
которых протокол SSH не столь распространен, решающим фактором становится
простота применения. К тому же, рассматриваемый протокол работает быстрее
и рациональнее, чем SSH.
Кроме того, доступ на чтение репозитория можно организовать по протоколу
HTTPS, дающему возможность шифровать трафик. Можно даже заставить клиентов
пользоваться специальными подписанными SSL-сертификатами.
Еще одним плюсом является тот факт, что благодаря широкой распространенности
протокола HTTP/S корпоративные фаерволы зачастую настроены на пропуск проходящего через эти порты трафика.

Недостатки
На некоторых серверах настроить Git по протоколу HTTP/S сложнее, чем по SSH.
Но в остальном при работе с Git другие протоколы демонстрируют мало преимуществ перед интеллектуальным протоколом HTTP.
Если вы используете протокол HTTP для авторизованной отправки информации на
сервер, постоянно вводить учетные данные сложнее, чем один раз воспользоваться

110    Глава 4 • Git на сервере
ключами в случае SSH. Однако существуют различные инструменты кэширования
учетных данных, например Keychain в OSX и Credential Manager в Windows, облегчающие эту процедуру.

Протокол SSH
В случае самостоятельного хостинга для Git чаще всего используется протокол SSH.
Дело в том, что доступ по SSH уже настроен на многих серверах, и даже если он
отсутствует, это легко можно исправить. Кроме того, SSH — это протокол с проверкой подлинности, а благодаря тому, что он используется повсеместно, его легко
настраивать и применять.
Для клонирования Git-репозитория по SSH указывается префикс ssh://URL, например:
$ git clone ssh://user@server:project.git

Протокол можно не указывать — в этом случае Git по умолчанию предполагает SSH:
$ git clone user@server:project.git

Кроме того, можно не указывать имя пользователя. В этом случае Git задействует
данные пользователя, авторизовавшегося в системе.

Достоинства
У протокола SSH множество достоинств. Во-первых, он легко настраивается —
SSH-демоны встречаются повсеместно, с ними умеют работать многие сетевые
администраторы, они уже настроены во многих дистрибутивах операционных
систем или же там есть инструменты для управления ими. Во-вторых, доступ по
SSH безопасен — все данные передаются в зашифрованном виде и с проверкой подлинности. Наконец, подобно протоколам HTTP/S, Git и локальному, SSH делает
данные перед передачей максимально компактными.

Недостатки
К сожалению, протокол SSH не дает возможности анонимного доступа к репозиторию. У каждого должен быть доступ к вашей машине по SSH, даже если речь идет
только о чтении, что совершенно не подходит для проектов с открытым исходным
кодом. Если работа осуществляется только в пределах вашей корпоративной сети,
протокол SSH может оказаться единственным, с которым вам придется иметь
дело. Если же требуется анонимный доступ на чтение материалов проекта, но при
этом вы хотите использовать SSH, отправка данных в репозиторий настраивается
по этому протоколу, а для их скачивания другими пользователями вам придется
придумать что-то другое.

Настройка Git на сервере   111

Протокол Git
Вместе с Git поставляется специальный демон, который слушает порт 9418.
Этот демон предоставляет сервис, напоминающий протокол SSH, но без необходимости аутентификации. Для работы репозитория по протоколу Git необходимо
создать файл git-export-daemon-ok, без него демон обслуживать репозиторий не
будет. Но о средствах безопасности в данном случае речь не идет. Репозиторий Git
может быть доступен для клонирования или всем, или никому. Это означает, что
в общем случае по этому протоколу данные на сервер отправлять нельзя. Если
открыть доступ на запись, то из-за отсутствия аутентификации кто угодно, зная
URL-адрес вашего проекта, сможет вносить туда изменения. Но такое бывает
крайне редко.

Достоинства
Зачастую протокол Git является самым быстрым из доступных сетевых протоколов
передачи данных. В случае проекта с открытым доступом и мощным трафиком
или большого проекта, в котором для получения доступа на чтение не требуется
аутентификация, скорее всего, вам имеет смысл настроить Git-демон. Он использует тот же самый механизм передачи данных, что и протокол SSH, но без затрат
на шифрование и аутентификацию.

Недостатки
Недостатком протокола Git является отсутствие аутентификации. Поэтому использовать его в качестве единственного средства доступа к проекту нежелательно. В общем случае он работает в паре с протоколом SSH или HTTPS, который
обеспечивает нескольким разработчикам доступ на передачу данных (запись),
в то время как нужды всех остальных обслуживает протокол git://, дающий
доступ только на чтение. Также это один из самых сложно настраиваемых протоколов. Для него необходим собственный демон, требующий настройки службы
xinetd или ей подобной, что далеко не всегда легко. Кроме того, фаервол должен
­предоставлять доступ к порту 9418, который не относится к стандартным портам,
всегда открытым корпоративными фаерволами. Неизвестные порты последними
обычно блокируются.

Настройка Git на сервере
А теперь мы поговорим о настройке Git-службы, запускающей эти протоколы
на вашем собственном сервере. Для начальной настройки любого Git-сервера
­требуется экспортировать существующий репозиторий в новый репозиторий,
пока не имеющий рабочей папки. Обычно это достаточно простая процедура.

112    Глава 4 • Git на сервере
Для ее выполнения вам потребуется команда clone с параметром --bare. По существующему соглашению папки с голыми репозиториями имеют расширение .git:
$ git clone --bare my_project my_project.git
Cloning into bare repository 'my_project.git'...
done.

ПРИМЕЧАНИЕ
В этом разделе демонстрируются команды и действия, необходимые для базовой,
упрощенной установки на сервере с операционной системой Linux, хотя данные службы
могут работать и на серверах с операционными системами Mac и Windows. Разумеется,
настройка рабочего сервера внутри конкретной инфраструктуры потребует разных мер
безопасности и своих инструментов в каждой системе, но в данном случае мы пытаемся
сформировать у вас общее представление о порядке действий.

Копия данных из папки Git должна появиться в папке my_project.git.
Это примерно эквивалентно следующей записи:
$ cp -Rf my_project/.git my_project.git

Конфигурационный файл в этом случае будет содержать пару небольших отличий,
но для наших целей они несущественны. Команда берет Git-репозиторий без его
рабочей папки и создает папку конкретно для него.

Размещение на сервере голого репозитория
Теперь, когда у вас есть копия голого репозитория, ее нужно поместить на сервер
и настроить протоколы. Мы предполагаем, что у вас уже есть полностью готовый
к работе сервер git.example.com с доступом по протоколу SSH, а все свои репозитории вы хотите поместить в папку /opt/git. Предполагается, что такая папка на
сервере уже существует, поэтому новый репозиторий можно получить простым
копированием:
$ scp -r my_project.git user@git.example.com:/opt/git

После этой операции другие пользователи, имеющие доступ на этот сервер по протоколу SSH и право читать содержимое папки /opt/git, могут клонировать ваш
репозиторий командой:
$ git clone user@git.example.com:/opt/git/my_project.git

Если у такого пользователя будут еще и права на запись в папку /opt/git/my_
project.git, у него автоматически появится возможность отправлять свои файлы
в репозиторий. Также автоматически Git корректно реализует права на запись для
группы, если запустить команду git init с параметром --shared:

Настройка Git на сервере   113
$ ssh user@git.example.com
$ cd /opt/git/my_project.git
$ git init --bare --shared

Вы видите, насколько просто взять Git-репозиторий, создать его голую версию и поместить на сервере, к которому у вас и ваших коллег есть доступ по протоколу SSH.
После этого вы можете совместно работать над общим проектом.
Важно отметить, что это все, что требуется сделать для получения рабочего Gitсервера с доступом для нескольких человек, — добавьте на этот сервер учетные
записи, умеющие работать с SSH, и поместите голый репозиторий в такое место,
чтобы у всех заинтересованных лиц были права на чтение и запись. Больше никаких
манипуляций не требуется.
Далее мы рассмотрим более сложные варианты настройки. К примеру, вы узнаете,
как обойтись без создания учетных записей для каждого пользователя, как обеспечить общий доступ на чтение репозиториев, как настроить пользовательские вебинтерфейсы, как работать с инструментом Gitosis и многое другое. Но помните, что
сервера с доступом по протоколу SSH и голого репозитория более чем достаточно,
если речь идет о работе небольшой группы над закрытым проектом.

Простые настройки
Если вы работаете в небольшой фирме или только пытаетесь внедрить у себя
Git, имея небольшое число разработчиков, процесс настройки будет достаточно
простым. Одним из самых сложных аспектов настройки Git-сервера является
управление пользователями. Ведь когда нужно дать некоторым пользователям
права только на чтение из репозиториев, а другим права на чтение и запись, задача
усложняется.

Доступ по протоколу SSH
При наличии сервера, к которому у всех разработчиков уже есть доступ по протоколу SSH, проще всего расположить первый репозиторий там, так как в этом
случае практически больше ничего не нужно делать (как вы могли убедиться в
предыдущем разделе). Но если требуются более сложные права доступа к репозиториям, можно реализовать это через обычные права доступа файловой системы,
используемой у вас на сервере.
Если же репозиторий будет располагаться на сервере, где не у всех членов рабочей
группы, которым требуются права на запись, есть учетные записи, доступ по протоколу SSH придется настраивать вручную. Предполагается, что сервер с доступом
по протоколу SSH у вас уже есть.
Существуют разные способы предоставления доступа членам группы. Во-первых,
можно создать для каждого из них учетную запись. Это самое простое, что можно

114    Глава 4 • Git на сервере
сделать, но процедура может оказаться утомительной. Вряд ли вам захочется каждый
раз выполнять команду adduser и придумывать временные пароли.
Во-вторых, можно создать на рабочей машине единого пользователя с именем git,
попросить каждого, кому требуется доступ на запись, прислать свой открытый
ключ SSH и добавить все присланные ключи в файл ~/.ssh/authorized_keys
нового пользователя git. Именно через этого пользователя все получат доступ к
компьютеру. Это никак не повлияет на данные коммитов — учетная запись, из которой вы соединяетесь с сервером по протоколу SSH, не затрагивает записанные
вами коммиты.
Еще можно сделать так, чтобы SSH-сервер опознавал вас через LDAP-сервер или
другое централизованное средство аутентификации, которое у вас уже есть. При
условии доступа каждого пользователя к консоли будет работать любой механизм
аутентификации по протоколу SSH.

Создание открытого ключа SSH
Как уже говорилось, многие Git-серверы производят аутентификацию по открытым ключам SSH. Каждый пользователь вашей системы должен сгенерировать
себе такой ключ, если он пока отсутствует. Этот процесс происходит одинаково во
всех операционных системах. Первым делом нужно убедиться в отсутствии ключа.
По умолчанию пользовательские SSH-ключи хранятся в папке ~/.ssh каждого
пользователя. Проверить, есть ли ключ, можно, зайдя в эту папку и отобразив ее
содержимое:
$ cd ~/.ssh
$ ls
authorized_keys2 id_dsa known_hosts
config id_dsa.pub

Нам нужна пара файлов с такими именами, как, к примеру, id_dsa или id_rsa,
и соответствующий файл с расширением .pub. Файл с расширением .pub содержит
открытый ключ, а второй файл — закрытый ключ. Если таких файлов нет (а может
быть, нет и папки .ssh), их можно создать в программе ssh-keygen, которая в операционных системах Linux/Mac поставляется вместе с пакетом SSH, а в Windows —
вместе с пакетом MSysGit:
$ ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/schacon/.ssh/id_rsa):
Created directory '/home/schacon/.ssh'.
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /home/schacon/.ssh/id_rsa.
Your public key has been saved in /home/schacon/.ssh/id_rsa.pub.

Настройка сервера   115
The key fingerprint is:
d0:82:24:8e:d7:f1:bb:9b:33:53:96:93:49:da:9b:e3 schacon@mylaptop.local

Первым делом вы указываете, где вы хотите сохранить ключ (.ssh/id_rsa), затем
вас дважды просят ввести парольную фразу. Если вы не хотите вводить пароль,
когда используете ключ, пропустите эту операцию.
Теперь ключ следует отослать администратору Git-сервера (предполагается, что
настройки вашего SSH-сервера требуют открытых ключей). Для этого нужно скопировать содержимое файла .pub и отправить его по электронной почте. Открытые
ключи выглядят примерно так:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
t3FaoJoAsncM1Q9x5+3V0Ww68/eIFmb1zuUFljQJKprrX88XypNDvjYNby6vw/Pb0rwert/En
mZ+AW4OZPnTPI89ZPmVMLuayrD2cE86Z/il8b+gw3r3+1nKatmIkjn2so1d01QraTlMqVSsbx
NrRFi9wrf+M7Q== schacon@mylaptop.local

Более подробную инструкцию по созданию SSH-ключей в разных операционных
системах можно найти в руководстве GitHub по SSH-ключам, которое находится
по адресу https://help.github.com/articles/generating-ssh-keys.

Настройка сервера
Рассмотрим настройку доступа по протоколу SSH на стороне сервера. В нашем примере для аутентификации пользователей будет применяться метод authorized_keys.
Предполагается, что у вас установлен стандартный дистрибутив Linux, например
Ubuntu. Первым делом нужно создать пользователя git и папку .ssh для этого
пользователя:
$
$
$
$

sudo adduser git
su git
cd
mkdir .ssh

Затем для этого пользователя нужно добавить открытые SSH-ключи какого-либо разработчика в файл authorized_keys. Предположим, что вы получили по
электронной почте несколько ключей и сохранили их во временных файлах. Еще
раз напомним, как выглядят открытые ключи:
$ cat /tmp/id_rsa.john.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCB007n/ww+ouN4gSLKssMxXnBOvf9LGt4L
ojG6rs6hPB09j9R/T17/x4lhJA0F3FR1rP6kYBRsWj2aThGw6HXLm9/5zytK6Ztg3RPKK+4k
Yjh6541NYsnEAZuXz0jTTyAUfrtU3Z5E003C4oxOj6H0rfIF1kKI9MAQLMdpGW1GYEIgS9Ez
Sdfd8AcCIicTDWbqLAcU4UpkaX8KyGlLwsNuuGztobF8m72ALC/nLF6JLtPofwFBlgc+myiv
O7TCUSBdLQlgMVOFq1I2uPWQOkOWQAHukEOmfjy2jctxSDBQ220ymjaNsHT4kgtZg2AYYgPq
dAv8JggJICUvax2T9va5 gsg-keypair

116    Глава 4 • Git на сервере
Вы просто добавляете эти ключи в свой файл authorized_keys:
$ cat /tmp/id_rsa.john.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.josie.pub >> ~/.ssh/authorized_keys
$ cat /tmp/id_rsa.jessica.pub >> ~/.ssh/authorized_keys

Теперь можно настроить для них голый репозиторий, запустив команду git init
с параметром –bare. В таком виде она инициализирует репозиторий без рабочей
папки:
$ cd /opt/git
$ mkdir project.git
$ cd project.git
$ git init --bare
Initialized empty Git repository in /opt/git/project.git/

Теперь пользователи John, Josie и Jessica могут добавить этот репозиторий как
удаленный и отправить туда первую версию проекта, над которым они работают.
Имейте в виду, что каждый раз, когда вы хотите добавить проект, кто-то должен
создавать на сервере голый репозиторий. Пусть сервер, на котором вы настроили
пользователя git и репозиторий, называется gitserver. Если он находится во
внутренней сети и вы настроили ссылающуюся на этот сервер DNS-запись для
gitserver, командами можно пользоваться следующим образом:
#
$
$
$
$
$
$

на компьютере пользователя John
cd myproject
git init
git add.
git commit -m 'initial commit'
git remote add origin git@gitserver:/opt/git/project.git
git push origin master

Остальные могут без проблем клонировать это хранилище и отправлять туда изменения:
$
$
$
$

git
vim
git
git

clone git@gitserver:/opt/git/project.git
README
commit -am 'fix for the README file'
push origin master

Этот способ позволяет быстро настроить Git-сервер с доступом на чтение и запись
для небольшой группы разработчиков.
Следует заметить, что в настоящее время все эти пользователи также могут авторизоваться на сервере и получить доступ к консоли как пользователь git. Если вы
хотите ограничить эту возможность, поменяйте в файле passwd командную оболочку на что-то другое.
Ограничить активность пользователя git только действиями, связанными с Git, позволяет поставляемая с этой системой ограниченная оболочка git-shell. Установив
ее в качестве оболочки для авторизации пользователя git, вы ограничите доступ

Git-демон   117

этого пользователя к обычной оболочке. Для этого нужно указать git-shell вместо
bash или csh как оболочку авторизации в файле /etc/passwd:
$ sudo vim /etc/passwd

В нижней части этого файла найдите примерно такую строку:
git:x:1000:1000::/home/git:/bin/sh

Замените /bin/sh на /usr/bin/git-shell (или запустите команду which gitshell, чтобы посмотреть, куда эта оболочка установлена). После редактирования
строка примет вид:
git:x:1000:1000::/home/git:/usr/bin/git-shell

Теперь пользователь Git сможет использовать SSH-соединение только для обмена
данными с Git-репозиториями. При попытке же зайти на сервер через консоль он
получит отказ в авторизации:
$ ssh git@gitserver
fatal: Interactive git shell is not enabled.
hint: ~/git-shell-commands should exist and have read and execute access.
Connection to gitserver closed.

Сетевые Git-команды будут работать, но на сервер пользователи зайти не смогут.
Как следует из вывода команды, вы можете изменить домашний каталог пользователя git, что слегка изменит поведение команды git-shell. К примеру, можно
ограничить количество принимаемых сервером Git-команд или отредактировать
сообщение, которое увидят пользователи при попытке зайти на сервер по протоколу SSH. Для получения дополнительной информации по настройке оболочки
используйте команду git help shell.

Git-демон
Теперь рассмотрим процесс настройки демона, обслуживающего репозитории по
протоколу Git. Это распространенный вариант доступа к Git-данным без аутентификации. Напоминаем, что в этом случае все обслуживаемое по данному протоколу
общедоступно в сети.
Если сервер, на котором запущен демон, находится не за фаерволом, использовать
его можно только для открытых проектов. Находящийся под защитой фаервола
сервер может обеспечивать доступ на чтение большому числу людей или компьютеров (серверы непрерывной интеграции или сборки), если вы не хотите добавлять
SSH-ключи для всех пользователей.

118    Глава 4 • Git на сервере
В любом случае настройка протокола Git осуществляется достаточно просто.
По сути, достаточно этой команды:
git daemon --reuseaddr --base-path=/opt/git/ /opt/git/

Параметр --reuseaddr позволяет серверу перезагрузиться, не ожидая истечения
времени ожидания старого подключения, а благодаря параметру --base-path пользователи могут клонировать проекты без указания полного пути — путь в конце
команды сообщает демону Git, где находятся экспортируемые репозитории. Если
вы используете фаервол, в нем должен быть открыт порт 9418 на компьютере, на
котором вы все это настраиваете.
Автоматизировать запуск процесса в режиме демона можно разными способами,
зависящими от операционной системы, с которой вы работаете. На машине с Ubuntu
можно использовать сценарий Upstart:
/etc/event.d/local-git-daemon

В этот файл помещается такой сценарий:
start on startup
stop on shutdown
exec /usr/bin/git daemon \
--user=git --group=git \
--reuseaddr \
--base-path=/opt/git/ \
/opt/git/
respawn

По соображениям безопасности запускать этого демона желательно как пользователя с правами только на чтение репозиториев. Для этого нужно создать нового
пользователя git-ro и запустить из-под него демона. Для простоты мы запустим
его из-под того же пользователя, из-под которого запущена ограниченная оболочка.
После перезапуска компьютера демон Git стартует автоматически. Автоматическим
является и его перезапуск после завершения работы. Без перезагрузки его можно
привести в активное состояние командой:
initctl start local-git-daemon

В других операционных системах можно воспользоваться сценарием xinetd системы
инициализации sysvinit или чем-то еще, — главное, чтобы команду можно было
запустить как демона и каким-то образом за ней присматривать.
Затем нужно указать, к каким репозиториям предоставляется доступ через Gitсервер без аутентификации. Для каждого репозитория при этом создается файл
git-daemon-export-ok.
$ cd /path/to/project.git
$ touch git-daemon-export-ok

Наличие этого файла является для Git сигналом того, что с проектом можно работать без аутентификации.

Интеллектуальный протокол HTTP   119

Интеллектуальный протокол HTTP
Итак, мы организовали аутентифицированный доступ по протоколу SSH и доступ
без аутентификации по протоколу git://, но существует и протокол, позволяющий
объединить эти вещи. Настройка этого протокола, по сути, представляет собой добавление на сервер поставляемого вместе с Git CGI-сценария git-http-backend.
Этот сценарий считывает путь и заголовки, пересылаемые в URL-адресах, которые
передаются по протоколу HTTP командами git fetch и git push, и определяет,
может ли клиент пользоваться этим протоколом для работы (все клиенты, начиная с версии 1.6.6, допускают эту возможность). Если выяснится, что клиент не в
состоянии работать с более новой, интеллектуальной версией протокола, для него
будет взята старая простая версия (то есть поддерживается обратная совместимость,
обеспечивающая старым версиям клиента возможность чтения).
Рассмотрим основные этапы установки. В данном случае в качестве CGI-сервера
будет фигурировать Apache. Читатели, у которых нет доступа к Apache, могут проделать на Linux-машине примерно следующее:
$ sudo apt-get install apache2 apache2-utils
$ a2enmod cgi alias env

При этом будут активированы необходимые для корректной работы модули mod_cgi,
mod_alias и mod_env.
Теперь внесем дополнения в конфигурацию Apache, так как нам требуется превратить сценарий git-http-backend в обработчик всей информации, приходящей по
пути /git вашего веб-сервера.
SetEnv GIT_PROJECT_ROOT /opt/git
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias /git/ /usr/libexec/git-core/git-http-backend/

Без переменной среды GIT_HTTP_EXPORT_ALL Git будет только отдавать не прошедшим аутентификацию клиентам репозитории, содержащие файл git-daemonexport-ok, как это делает Git-демон.
Затем нужно сделать так, чтобы сервер Apache разрешил запросы к данному пути:

Options ExecCGI Indexes
Order allow,deny
Allow from all
Require all granted


Наконец, следует установить аутентификацию на запись, например при помощи
вот такого блока Auth:

AuthType Basic
AuthName "Git Access"
AuthUserFile /opt/git/.htpasswd

120    Глава 4 • Git на сервере
Require valid-user


Для этого потребуется файл .htaccess с паролями всех имеющих доступ пользователей. Вот как выглядит добавление в этот файл пользователя schacon:
$ htdigest -c /opt/git/.htpasswd «Git Access» schacon

Существует множество способов аутентификации пользователей на сервере Apache,
вам остается выбрать и реализовать какой-нибудь из них. Мы привели простейший
пример. Мы почти уверены, что заодно вы захотите настроить работу по протоколу
SSL для обеспечения шифрования данных.
Мы не будем слишком детально рассматривать особенности конфигурации
серверов Apache, так как вы можете работать с другим сервером и иметь другие
требования к аутентификации. Основная идея состоит в наличии у системы Git
CGI-сценария git-http-backend, который берет на себя все процедуры согласования для отправки и приема данных по протоколу HTTP. Сам по себе этот
сценарий не выполняет аутентификацию, но легко может контролироваться на
уровне запускающего его веб-сервера. Его можно использовать практически на
любом веб-сервере, поддерживающем CGI, так что вы можете выбрать наиболее
удобный для вас вариант.
ПРИМЕЧАНИЕ
Дополнительные сведения о настройке аутентификации в Apache вы найдете в документации по адресу: http://httpd.apache.org/docs/current/howto/auth.html.

Интерфейс GitWeb
Итак, вы настроили для вашего проекта базовый доступ на чтение/запись и доступ только на чтение и, возможно, не отказались бы от простого веб-интерфейса.
Система Git поставляется с CGI-сценарием GitWeb, который порой используется
именно для этой цели (рис. 4.1).
В Git существует команда, позволяющая владельцам облегченных серверов, таких как lighttpd или webrick, установить временный экземпляр GitWeb, чтобы
посмотреть, как он будет выглядеть в случае конкретного проекта. На машинах с
операционной системой Linux сервер lighttpd зачастую уже установлен, так что
достаточно выполнить команду git instaweb из папки вашего проекта. Операционная система Leopard на компьютерах Mac поставляется с предустановленным
языком Ruby, поэтому, наверное, в этом случае лучше пользоваться сервером
webrick. По умолчанию команда instaweb вызывает обработчик lighttpd, поэтому
вам потребуется параметр --httpd.

Интерфейс GitWeb   121

Рис. 4.1. Пользовательский веб-интерфейс на базе GitWeb
$ git instaweb --httpd=webrick
[2009-02-21 10:02:21] INFO WEBrick 1.3.1
[2009-02-21 10:02:21] INFO ruby 1.8.6 (2008-03-03) [universal-darwin9.0]

Запустится HTTPD-сервер на порту 1234, и автоматически откроется веб-браузер
с нужной страницей. Как видите, все очень просто. Когда вы завершите работу, для
остановки сервера нужно будет выполнить эту же команду с параметром --stop:
$ git instaweb --httpd=webrick --stop

Если вашей группе или вашему проекту с открытым исходным кодом требуется
постоянно работающий веб-интерфейс, установите этот CGI-сценарий на своем
обычном веб-сервере. В некоторых дистрибутивах Linux имеется пакет gitweb,
который можно установить с помощью программ apt или yum, — возможно, первым
делом вы захотите испробовать этот способ. Мы же кратко рассмотрим процесс
ручной установки GitWeb. Для начала следует получить исходный Git-код, с которым поставляется GitWeb, и сгенерировать CGI-сценарий для своей системы:
$ git clone git://git.kernel.org/pub/scm/git/git.git
$ cd git/
$ make GITWEB_PROJECTROOT="/opt/git" prefix=/usr gitweb
SUBDIR gitweb

122    Глава 4 • Git на сервере
SUBDIR ../
make[2]: `GIT-VERSION-FILE' is up to date.
GEN gitweb.cgi
GEN static/gitweb.js
$ sudo cp -Rf gitweb /var/www/

Напоминаем, что команде следует указать местоположение ваших Git-репозиториев.
Это делается при помощи переменной GITWEB_PROJECTROOT. Далее нужно сделать
так, чтобы сервер Apache использовал для этого CGI-сценарий, для чего можно
добавить директиву VirtualHost:

ServerName gitserver
DocumentRoot /var/www/gitweb

Options ExecCGI +FollowSymLinks +SymLinksIfOwnerMatch
AllowOverride All
order allow,deny
Allow from all
AddHandler cgi-script cgi
DirectoryIndex gitweb.cgi



Еще раз напомним, что интерфейс GitWeb может быть установлен на любом вебсервере, умеющем работать с CGI или Perl; если вы предпочитаете какой-то другой
сервер, настройка не должна вызвать у вас трудностей. После этого вы сможете
просматривать свои репозитории в сети по адресу http://gitserver/.

Приложение GitLab
Интерфейс GitWeb чрезмерно упрощен. В качестве более современного, полнофункционального Git-сервера можно взять одно из существующих решений с
открытым исходным кодом. Наиболее популярным из них является приложение
GitLab, поэтому именно его мы рассмотрим в качестве примера. Оно несколько
сложнее интерфейса GitWeb и требует куда большей поддержки, но предлагает
несоизмеримо более богатую функциональность.

Установка
Так как GitLab представляет собой веб-приложение на основе базы данных, установить его сложнее, чем другие Git-серверы. К счастью, для этого процесса существует
подробная документация и он хорошо поддерживается.
Существуют разные методы установки GitLab. Если требуется быстрый результат,
можно скачать с сайта https://bitnami.com/stack/gitlab образ виртуальной машины или

Приложение GitLab   123

быстрый установщик и отредактировать его конфигурацию под конкретную среду.
Авторы проекта Bitnami добавили такую замечательную возможность, как экран
входа (вызывается комбинацией клавиш Alt и →); на нем для установленного приложения GitLab указывается IP-адрес, а также по умолчанию имя пользователя
и пароль (рис. 4.2).

Рис. 4.2. Экран входа виртуальной машины GitLab от Bitnami

Остальные инструкции вы найдете в файле readme, созданном членами GitLabсообщества и доступном по адресу https://gitlab.com/gitlab-org/gitlab-ce/tree/master. Там
вы узнаете, как установить GitLab, используя рекомендации системы управления
Chef, виртуальную машину на сервере Digital Ocean, а также пакеты RPM и DEB
(которые на момент написания книги были представлены в виде бета-версий).
Также там есть «неофициальная» инструкция по запуску GitLab в случае нестандартных операционных систем и баз данных, полностью ручной сценарий установки
и многое другое.

Администрирование
Доступ к административному интерфейсу GitLab осуществляется через Интернет.
Укажите своему браузеру имя хоста или IP-адрес, где установлено приложение
GitLab, и авторизуйтесь с правами администратора. По умолчанию для этого предлагаются имя пользователя admin@local.host и пароль 5iveL!fe (и сразу же после
входа вам будет предложено их изменить). После этого нужно щелкнуть на значке
Admin area, расположенном в верхнем левом углу меню (рис. 4.3).

Рис. 4.3. Пункт Admin area в меню GitLab

124    Глава 4 • Git на сервере

Пользователи
Пользователи представлены в GitLab в виде набора учетных записей. Эти записи не
очень сложные и состоят по большей части из персональной информации, связанной с данными входа в систему. Каждой учетной записи соответствует собственное
пространство имен, логически объединяющее принадлежащие конкретному пользователю проекты (рис. 4.4). Скажем, URL-адрес проекта project пользователя
jane будет выглядеть так: http://server/jane/project.
Удалить учетную запись можно двумя способами. Блокировка пользователя не дает
ему авторизоваться в экземпляре GitLab, но в соответствующем пространстве имен
сохранятся все его данные, а подписанные его адресом электронной почты коммиты
будут содержать ссылки на его профиль.
Уничтожение же пользователя удаляет все его следы из базы данных и файловой
системы. Исчезают как все проекты и данные из его пространства имен, так и все
принадлежащие ему группы. Разумеется, этот радикальный и необратимый способ
применяется куда реже.

Рис. 4.4. Экран управления пользователями в GitLab

Группы
Группа в GitLab представляет собой набор проектов вместе с данными о том, как
пользователи могут получить доступ к этим проектам (рис. 4.5). Каждой группе соответствует пространство имен проекта (аналогичное пространству имен

Приложение GitLab   125

пользователей), то есть группа training с проектом materials будет доступна по
адресу http://server/training/materials.

Рис. 4.5. Экран управления группами в GitLab

С группой связан набор пользователей, каждый из которых имеет свой уровень
доступа к проектам группы и самой группе. Диапазон уровней варьируется от «гостя» (возможность задавать вопросы и принимать участие в чате) до «владельца»
(полный контроль группы, ее членов и ее проектов). Количество уровней доступа
слишком велико, чтобы перечислять их на страницах нашей книги, но на административном экране GitLab есть ссылка на их детальное описание.

Проекты
Проект в GitLab с некоторыми допущениями можно сопоставить с одним Gitрепозиторием. Каждый проект является частью одного пространства имен, группового или пользовательского. Если проект принадлежит пользователю, доступ
к нему контролирует владелец; в групповых проектах существуют допуски для
разных категорий пользователей.
Кроме того, у каждого проекта есть свой уровень видимости, определяющий, у кого
есть доступ к чтению страниц проекта или репозитория. В случае закрытого (private)
проекта владелец в явном виде предоставляет доступ определенным пользователям. Внутренний (internal) проект виден любому авторизованному пользователю,
а открытый (public) проект могут просматривать все желающие. Это относится как
к информации, получаемой посредством команды git fetch, так и к просмотру
проекта через веб-интерфейс.

126    Глава 4 • Git на сервере

Хуки
Поддержка хуков в GitLab реализована на уровне как проектов, так и системы
в целом. В обоих случаях при возникновении заданного события GitLab-сервер
передает по протоколу HTTP запрос POST с описанием в формате JSON. Это
замечательный способ объединить ваши Git-репозитории и экземпляр GitLab с
остальной автоматизированной структурой разработки, например серверами непрерывной интеграции, комнатами чатов или инструментами развертывания.

Базовое применение
Установив GitLab, первым делом создают новый проект. Для этого нужно щелк­
нуть на кнопке со значком + на панели инструментов приложения. Вам будет
предложено указать имя проекта, пространство имен, которому он должен принадлежать, и уровень его видимости. Большую часть этих данных в будущем при
желании можно изменить через интерфейс настроек. Щелкните на кнопке Create
Project, чтобы создать проект.
Существующие проекты вы, скорее всего, захотите соединить с локальным Gitрепозиторием. Все проекты доступны по протоколу HTTPS или SSH. Соответственно оба этих протокола могут применяться для настройки удаленного Gitрепозитория. Адреса URL можно увидеть в верхней части домашней страницы
проекта. Вот команда, которая длясуществующего локального репозитория создает
удаленный репозиторий с именем gitlab на указанном вами сервере:
$ git remote add gitlab https://server/namespace/project.git

Если локальная копия репозитория отсутствует, ее можно легко создать:
$ git clone https://server/namespace/project.git

Пользовательский веб-интерфейс обеспечивает доступ к разным представлениям
репозитория. На домашней странице каждого проекта отображается информация
о последних действиях, а ссылки в ее верхней части ведут к файлам проекта и журналу регистрации коммитов.

Совместная работа
Самым простым способом совместной работы над GitLab-проектом является предоставление другому пользователю непосредственного доступа к Git-репозиторию.
Добавление пользователей происходит в разделе Members окна настроек проекта,
где указывается уровень доступа каждого нового пользователя (уровни доступа

Сторонний хостинг   127

кратко обсуждались в разделе «Группы»). Уровень разработчика (developer) и выше
позволяет отправлять коммиты и ветки непосредственно в репозиторий.
Менее плотным способом совместной работы являются запросы на слияние. Эта
функциональность позволяет любому пользователю, который видит проект, контролируемым образом вносить в него свой вклад. Пользователи с непосредственным
доступом могут создавать ветки, присылать коммиты для этих веток и открывать
запросы на слияние из своих веток в ветку master или любую другую. Пользователи,
не имеющие доступа на запись в репозиторий, могут создать собственную копию
репозитория, отправить туда коммиты и открыть запрос на слияние их копии с
основным проектом. Эта модель позволяет владельцу репозитория, принимая
помощь от ненадежных пользователей, полностью контролировать, что именно
и когда попадает в репозиторий.
Запросы на слияние и проблемы (issues) являются основой длительных обсуждений в GitLab. Каждый запрос на слияние порождает как построчное обсуждение
предлагаемых изменений (чему способствует облегченное рецензирование кода),
так и дискуссию в целом. Оба варианта могут быть как назначены пользователям,
так и превращены в промежуточные этапы (milestones).
В этом разделе мы рассмотрели в основном аспекты GitLab, имеющие отношение
к Git, но это хорошо продуманная система с массой функциональных возможностей,
которые могут пригодиться при совместной работе. Сюда относятся, например,
вики-страницы проектов, дискуссионные «стены» и инструменты поддержки
системы.
Весомое преимущество GitLab состоит в том, что когда сервер настроен и работает,
необходимость вносить изменения в конфигурационный файл или обращаться к
нему по протоколу SSH возникает крайне редко; большая часть действий, связанных
с администрированием и использованием этого приложения, выполняется через
встроенный в браузер интерфейс.

Сторонний хостинг
Для тех, кто не хочет заниматься настройкой собственного Git-сервера, суще­
ствуют несколько вариантов размещения проектов на специальных сайтах. У такого подхода есть ряд преимуществ: настройка и запуск проекта на таком сайте
обычно не требуют много времени и вам не приходится заниматься поддержкой
сервера и наблюдением за ним. Даже при наличии собственного внутреннего
сервера ­в случае проектов с открытым исходном кодом сторонний хостинг порой оказывается более предпочтительным — в таком виде сообществу проще
оказывать вам помощь.

128    Глава 4 • Git на сервере
В наши дни существует огромное количество вариантов хостинга с различными
достоинствами и недостатками. Их список доступен по адресу https://git.wiki.kernel.
org/index.php/GitHosting.
Работа с крупнейшим на сегодняшний день хостингом GitHub подробно рассмотрена в главе 6, так как вам может потребоваться взаимодействие с расположенными
на нем проектами. Но есть и множество других вариантов для тех, кто не хочет
заниматься настройкой собственного Git-сервера.

Заключение
Существуют различные варианты устройства удаленного Git-репозитория, позволяющего принимать участие в совместных проектах и делиться собственными
наработками.
Собственный сервер дает вам полный контроль над происходящим; кроме того,
его можно защитить своим фаерволом, но вам потребуется изрядное количество
времени на его настройку и поддержку. Размещение данных с помощью стороннего
хостинга упрощает эти операции, но код в этом случае будет храниться на чужом
сервере, а далеко не все организации допускают подобное.
В данном случае вам остается только выбрать решение, устраивающее одновременно
и вас, и фирму, в которой вы работаете.

5

Распределенная
система Git

Итак, вы обзавелись удаленным Git-репозиторием, посредством которого все разработчики могут обмениваться своим кодом, и освоили основные Git-команды
для локальной работы. Значит, пришло время познакомиться с распределенными
рабочими процессами, которые вам предлагает Git.
В этой главе вы научитесь работать с Git в распределенной среде в качестве как простого разработчика, так и системного интегратора. То есть вы узнаете, как вносить
вклад в проекты, максимально упрощая эту процедуру для себя и для пользователя,
отвечающего за поддержку проекта, а также получите навыки сопровождения проектов, над которыми работает большой коллектив.

Распределенные рабочие процессы
В отличие от централизованных систем контроля версий (CVCS), Git благодаря
своему распределенному характеру допускает большую гибкость при совместной
работе над проектами. В централизованных системах всех разработчиков можно
представить в виде узлов сети, каждый из которых делает свою часть работы на
центральном концентраторе. В Git же каждый разработчик может выступать как
в роли узла, так и в роли концентратора. То есть он может вносить код в чужие
репозитории и поддерживать открытый репозиторий. Такой подход дает рабочей
группе широкие возможности. Некоторые распространенные варианты работы
над проектами, доступные благодаря гибкости системы Git, рассмотрены в этой

130    Глава 5 • Распределенная система Git
главе. Мы постараемся учесть как сильные стороны, так и возможные недостатки
каждого варианта, в результате вы сможете выбрать для себя наиболее подходящий
или скомбинировать функциональные возможности сразу нескольких походов.

Централизованная работа
В централизованных системах, как правило, существует всего одна модель совместной работы. Один центральный концентратор (репозиторий) принимает код,
а все остальные синхронизируют с ним свои действия. Все разработчики являются
узлами — клиентами этого концентратора — и сверяются только с ним (рис. 5.1).

Репозиторий общего доступа

Разработчик

Разработчик
Разработчик

Рис. 5.1. Централизованный рабочий процесс

Если два разработчика клонируют концентратор и каждый из них вносит изменения, первый может без проблем отправить результаты своего труда назад. А вот
второй, чтобы избежать перезаписи внесенных коллегой изменений, перед этим
должен выполнить слияние с его наработками. В Git этот принцип справедлив в
той же степени, что и в Subversion (или любой другой централизованной системе
контроля версий), — модель превосходно функционирует.
Если в своей фирме или группе вы уже привыкли к централизованному рабочему
процессу, можете работать с Git в аналогичной манере. Настройте один репозиторий
и предоставьте всем членам группы доступ на запись; система Git сама не позволит
пользователям переписывать наработки друг друга. Представим, что Джон и Джессика начинают работать одновременно. Джон вносит изменение и отправляет его
на сервер. На попытку Джессики сделать то же самое сервер ответит отказом. Он
сообщит, что Джессика пытается добавить изменения, для которых невозможно
выполнить перемотку, поэтому сначала ей нужно скачать данные с сервера, осуществить слияние и только после этого отправлять результаты своего труда. Такой
вариант многим по душе, потому что он знаком и привычен.
И это касается не только небольших групп. Модель ветвлений в Git позволяет
сотням разработчиков успешно трудиться в рамках одного проекта, одновременно
работая над десятками веток.

Распределенные рабочие процессы   131

Диспетчер интеграции
Так как количество удаленных репозиториев в Git неограниченно, рабочий процесс можно организовать так, чтобы у каждого разработчика было право на запись
в собственный открытый репозиторий и право на чтение чужих репозиториев.
Этот сценарий зачастую подразумевает наличие общего репозитория, представляющего собой «официальный» проект. Чтобы принять участие в работе над таким
проектом, нужно создать его открытую копию, куда и будут отправляться все
изменения. Позже пользователю, отвечающему за поддержку основного проекта,
отправляется запрос на внесение ваших изменений. После этого ваш репозиторий
могут добавить как удаленный, протестировать ваши изменения локально, слить
их со своей веткой и добавить в свой репозиторий (рис. 5.2). Вот общая последова­
тельность действий:
1. Владелец проекта выкладывает файлы в открытый репозиторий.
2. Участники проекта клонируют этот репозиторий и вносят свои изменения.
3. Каждый участник выкладывает изменения в свой собственный открытый
репозиторий.
4. Каждый участник отправляет владельцу письмо с просьбой включить его изменения в общий проект.
5. Владелец добавляет репозиторий каждого участника как удаленный и выполняет локальное слияние.
6. Владелец отправляет слитые изменения в основной репозиторий.

Основной
репозиторий
проекта

Открытый
репозиторий
разработчика

Открытый
репозиторий
разработчика

Диспетчер
интеграции

Закрытый
репозиторий
разработчика

Закрытый
репозиторий
разработчика

Рис. 5.2. Рабочий процесс с диспетчером интеграции

Рабочий процесс такого типа широко применяется с такими инструментами, как
GitHub или GitLab, работающими по принципу концентратора и позволяющими
легко выполнить ветвление проекта и выложить изменения на всеобщее обозрение
в собственную ветвь. Одним из основных преимуществ такого подхода является

132    Глава 5 • Распределенная система Git
возможность продолжать работу, в то время как владелец основного репозитория
может в любой момент включить туда ваши изменения. Участники проекта не должны ждать момента, когда их изменения будут встроены в общий проект, — каждый
может работать в собственном темпе.

Диктатор и помощники
Существует еще одна разновидность рабочего процесса с набором репозиториев.
Как правило, она применяется в крупных проектах с сотнями участников, таких
как работа над ядром Linux. За отдельные части репозитория отвечают несколько
диспетчеров интеграции; этих людей называют помощниками. Над ними главенствует один диспетчер интеграции, которого называют диктатором. Именно он
владеет эталонным репозиторием, откуда все остальные участники проекта должны
скачивать изменения (рис. 5.3). Вот описание рабочего процесса:
1. Обычные разработчики трудятся в тематических ветках и перемещают результаты своей работы на вершину ветки master. Ветка master принадлежит
диктатору.
2. Помощники сливают тематические ветки разработчиков в свои ветки
master.
3. Диктатор сливает содержимое веток master своих помощников в свою ветку
master.
4. Диктатор отправляет свою ветку master в эталонный репозиторий, что дает
остальным разработчикам возможность перемещать туда свои данные.

Эталонный
репозиторий

Диктатор

Помощники

Помощники

Открытые
репозитории
разработчиков

Открытые
репозитории
разработчиков

Открытые
репозитории
разработчиков

Рис. 5.3. Рабочий процесс с диктатором и помощниками

Содействие проекту   133

Рабочий процесс этого типа встречается нечасто, но может быть полезен в очень
больших проектах или в сторогом иерархическом окружении. Он позволяет лидеру
проекта (диктатору) делегировать большую часть работы и собирать из разных мест
большие фрагменты кода перед его интеграцией.

Заключение
Мы рассмотрели несколько распространенных вариантов рабочего процесса, доступных для таких распределенных систем, как Git, но позже вы убедитесь, что
существует еще множество вариантов, зависящих от конкретных рабочих условий.
Надеемся, что теперь вы в состоянии выбрать подходящий лично вам вариант,
а значит, можно перейти к рассмотрению конкретных примеров реализации основных ролей в рамках рабочих процессов. В следующем разделе вы познакомитесь
с распространенными вариантами содействия проекту.

Содействие проекту
Описать процесс содействия проекту сложно, главным образом из-за наличия множества подходов к его организации. Крайняя гибкость Git обеспечивает большую
вариативность способов совместной работы, а значит, конкретный способ содействия
проекту будет зависеть от конкретной ситуации. Следует учитывать количество
активных участников, выбранную схему работы, ваши права доступа к коммитам,
а возможно, и способ принятия изменений от сторонних разработчиков.
Первый фактор — количество активных участников. Сколько пользователей добавляет к проекту свой код и насколько часто? Зачастую это два-три разработчика
с несколькими коммитами в день, а порой и меньше. В более крупных компаниях
или проектах число разработчиков может измеряться сотнями или даже тысячами
ежедневных коммитов. С увеличением числа разработчиков становится все труднее
гарантировать возможность аккуратного применения изменений или легкого выполнения слияний. Изменения, которые вы отправляете, могут оказаться устаревшими
или частично недействительными из-за данных, которые были вставлены в проект,
пока вы работали или пока результаты вашего труда ждали одобрения и применения.
Как сделать так, чтобы код оставался согласованным, а коммиты — актуальными?
Следующим фактором является выбранный для проекта тип рабочего процесса.
Это должен быть централизованный процесс с наличием у каждого разработчика
права на запись в главный репозиторий? Или существует человек, отвечающий за
поддержку проекта и проверку всех присланных исправлений перед их интеграцией?
Все ли исправления проверяются и одобряются специалистами? Вовлечены ли вы
в этот процесс? Присутствуют ли в системе помощники и нужно ли первым делом
отправлять свою работу им?

134    Глава 5 • Распределенная система Git
Следующий вопрос касается ваших прав на добавление коммитов. Схема вклада
в проект зависит от наличия у вас доступа на запись. Если его у вас нет, каким образом осуществляется вклад в проект? Существуют ли какие-либо правила и политики? Какой объем работы вы вносите за один раз? Насколько часто вы это делаете?
От ответов на эти вопросы зависят эффективность вашего содействия проекту
и оптимальный в вашей ситуации рабочий процесс. Все эти вещи мы рассмотрим
в виде набора примеров, переходя от простых к более сложным; надеемся, что данные иллюстрации в будущем позволят вам организовывать собственные варианты
рабочих процессов в зависимости от конкретных обстоятельств.

Рекомендации по созданию коммитов
Прежде чем мы приступим к рассмотрению конкретных сценариев, поговорим
немного о сообщениях фиксации. Подробная инструкция по созданию коммитов
значительно облегчает работу с Git и сотрудничество с другими разработчиками.
У Git-проекта есть документация с хорошими советами по созданию коммитов,
на основе которых делаются исправления. Ознакомиться с нею можно в файле
Documentation/SubmittingPatches, входящем в исходный Git-код.
Во-первых, в отправляемых вами фрагментах кода не должно быть ошибок, связанных с пробелами. В Git существует команда, легко позволяющая проверить
их наличие, — git diff --check. Обязательно применяйте ее перед отправкой
коммитов (рис. 5.4).

Рис. 5.4. Результат выполнения команды git diff --check

Эта команда сообщит, не собираетесь ли вы отправить коммит, содержащий так
раздражающие других разработчиков ошибки, связанные с пробелами.
Во-вторых, старайтесь, чтобы каждый коммит представлял собой логически обособленный набор изменений. Желательно, чтобы изменения были не очень громоздкими, — не стоит все выходные работать над пятью проблемами, чтобы в понедельник

Содействие проекту   135

отправить один огромный коммит. Даже если в течение выходных изменения не
фиксировались, в понедельник воспользуйтесь областью индексирования и разбейте
результаты своего труда на части, по одному коммиту на проблему с содержательным сообщением фиксации в каждом случае. Если какие-то изменения относятся
к одному и тому же файлу, попробуйте воспользоваться командой git add --patch
для индексации файлов по частям. Снимок состояния проекта на вершине ветки не
зависит от того, один коммит вы сделали или пять, если все изменения добавляются
одномоментно. Поэтому постарайтесь облегчить жизнь коллегам-разработчикам,
которые будут просматривать плоды ваших усилий. Кроме того, такой подход
упрощает процедуру извлечения или отмены одного изменения из внесенного набора, если вдруг в будущем появится такая необходимость.
Не следует забывать и про сообщение фиксации. Привычка к написанию содержательных сообщений упрощает работу с Git и облегчает сотрудничество. Как правило,
сообщение должно начинаться со строки с лаконичным описанием изменений,
содержащей не более 50 символов. Затем следует пустая строка и более подробное
описание. Git-проект требует включить в этот текст информацию о причине изменений и сопоставление вашей реализации с предыдущим поведением. Желательно
действовать в соответствии с этими рекомендациями. Кроме того, в этих сообщениях рекомендуют использовать повелительное наклонение. Другими словами,
следует писать команды. Вместо «я добавил тест для» пишите «добавьте тест для».
Вот пример шаблона, написанный Тимом Поупом (Tim Pope):
Краткое (до 50 символов) описание изменений
Более детальный, поясняющий текст, если он требуется. Ограничьтесь
примерно 72 символами. В некоторых контекстах первая строка
рассматривается как тема электронного письма, а остальной
текст — как тело письма. Важна пустая строка, отделяющая краткое
описание от тела письма (если тело письма присутствует);
в противном случае такие инструменты, как rebase, могут воспринять
написанное некорректно.
Дальнейшие абзацы идут после пустых строк.
- Возможны маркированные списки
- Роль маркера обычно играет дефис или звездочка,
с одним пробелом перед ними и пустыми строками между
пунктами, но возможны и другие реализации

Если ваши сообщения фиксации выглядят подобным образом, это весьма удобно
как для вас, так и для ваших коллег-разработчиков. Примеры хорошо сформулированных сообщений фиксации можно найти в Git-проекте — попробуйте запустить
для него команду git log --no-merges и почитать корректно отформатированную
историю коммитов проекта.
В примерах из этой книги вы таких сообщений не увидите. Это сделано для экономии места; вместо них мы применяли параметр -m команды git commit. Поэтому
не стоит считать примеры из книги руководством к действию.

136    Глава 5 • Распределенная система Git

Работа в маленькой группе
Простейшей организационной структурой, которая может вам встретиться, является закрытый проект с одним или двумя другими разработчиками. Определение
«закрытый» в данном случае используется потому, что создаваемый этой группой
код недоступен остальному миру. Право на запись в репозиторий есть как у вас,
так и у остальных разработчиков.
В такой среде возможен рабочий процесс, которого придерживаются при работе
с Subversion или другой централизованной системой. При этом у вас сохранятся
такие преимущества, как локальные коммиты и упрощенные процедуры слияния
и ветвления, но рабочий процесс будет отличаться в основном тем, что во время
фиксации изменений слияние выполняется на стороне клиента, а не на сервере.
Рассмотрим, как это выглядит, на примере двух разработчиков, пользующихся
общим репозиторием. Джон клонирует репозиторий, добавляет свои изменения и
фиксирует их локально. (Для сокращения фрагментов кода служебные сообщения
заменены многоточием.)
# Компьютер Джона
$ git clone john@githost:simplegit.git
Initialized empty Git repository in /home/john/simplegit/.git/
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'removed invalid default value'
[master 738ee87] removed invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)

Джессика — второй разработчик — делает то же самое, то есть клонирует репозиторий и фиксирует внесенные в копию изменения:
# Компьютер Джессики
$ git clone jessica@githost:simplegit.git
Initialized empty Git repository in /home/jessica/simplegit/.git/
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'add reset task'
[master fbff5bc] add reset task
1 files changed, 1 insertions(+), 0 deletions(-)

Затем Джессика отправляет свою работу на сервер:
# Компьютер Джессики
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master

Джон также пытается отправить на сервер результаты своего труда:
# Компьютер Джона
$ git push origin master

Содействие проекту   137
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'

Джон получает отказ в совершении операции, так как Джессика успела отправить свои изменения первой. Этот момент крайне важно понять, особенно если
вы привыкли работать с системой Subversion. Мы видим, что разработчики
редактировали разные файлы. В этом случае Subversion выполняет слияние на
сервере автоматически, а работая с Git, вы должны сделать это вручную на своей
машине. Джону следует скачать изменения, внесенные Джессикой, и выполнить
их слияние, и только после этого он получит разрешение на отправку результатов
своего труда:
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master

То, как при этом будет выглядеть локальный репозиторий Джона, показано на
рис. 5.5.
master

4b078

1edee

738ee
fbff5

origin/master

Рис. 5.5. Расходящаяся история Джона

У Джона есть ссылка на выложенные Джессикой изменения, и он должен осуществить их слияние со своей работой:
$ git merge origin/master
Merge made by recursive.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)

Слияние в данном случае происходит без проблем. История коммитов Джона начинает выглядеть так, как показано на рис. 5.6.
Теперь Джон может протестировать свой код, убедиться, что он по-прежнему
корректно работает, и выложить на сервер свои наработки, уже объединенные
с наработками Джессики:

138    Глава 5 • Распределенная система Git
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master

История коммитов Джона после этой операции показана на рис. 5.7.
В это время Джессика работала над тематической веткой. Она создала ветку issue54
и выполнила в ней три коммита. Внесенные Джоном изменения она пока не скачала, поэтому история ее изменений будет выглядеть так, как показано на рис. 5.8.
master

4b078

1edee

738ee

72bbc

fbff5

origin/master

Рис. 5.6. Репозиторий Джона после слияния с веткой origin/master

master

4b078

1edee

738ee
fbff5

72bbc

origin/master

Рис. 5.7. История коммитов Джона после отправки изменений на сервер origin

master

4b078

1edee

fbff5

issue54

8149a

23ac6

origin/master

Рис. 5.8. Исходная история коммитов Джессики

4af42

Содействие проекту   139

Чтобы синхронизировать свою работу с наработками Джона, Джессика скачивает
с сервера изменения:
# Компьютер Джессики
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master

После получения выложенных Джоном изменений история коммитов Джессики
выглядит так, как показано на рис. 5.9.
master

4b078

1edee

issue54

fbff5

8149a

738ee

72bbc

23ac6

4af42

origin/master

Рис. 5.9. История Джессики после скачивания изменений Джона

Джессика считает свою тематическую ветку законченной и хочет узнать, с чем
нужно слить ее работу, чтобы получить возможность отправить ее на сервер. Для
этого она использует команду git log:
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith
Date: Fri May 29 16:01:27 2009 -0700
removed invalid default value

Запись issue54..origin/master служит фильтром для команды log, указывающим,
что отображать нужно только те коммиты из последней ветки (в данном случае
это ветка origin/master), которых нет в первой ветке (в данном случае это ветка
issue54).
Эта команда показывает, что Джон успел зафиксировать одно изменение, которое
Джессика пока не добавила к своей работе. Если она сливает к себе ветку origin/
master, этот единственный коммит модифицирует ее локальные изменения.
После этого Джессика может слить свою тематическую ветку в свою ветку master,
туда же слить работу Джона (origin/master), а потом отправить все изменения
на сервер. Для интеграции всего этого она первым делом переходит в свою ветку
master:

140    Глава 5 • Распределенная система Git
$ git checkout master
Switched to branch "master"
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.

Теперь можно слить к себе ветку origin/master или ветку issue54 — обе они в
истории коммитов находятся выше, поэтому порядок слияния не играет роли.
Конечное состояние репозитория в обоих случаях получится идентичным, слегка
отличаться будет только история. Джессика решает сначала выполнить слияние
ветки issue54:
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++2 files changed, 6 insertions(+), 1 deletions(-)

Как видите, проблем не возникло. Это обычное слияние перемотки. Теперь Джессика добавляет к себе работу Джона (origin/master):
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by recursive.
lib/simplegit.rb | 2 +1 files changed, 1 insertions(+), 1 deletions(-)

Слияние проходит нормально, и история коммитов Джессики начинает выглядеть
так, как показано на рис. 5.10.
issue54

4b078

1edee

fbff5

8149a

738ee

72bbc

23ac6

master

4af42

8059c

origin/master

Рис. 5.10. История коммитов Джессики после добавления коммитов Джона

Теперь ветка origin/master достижима из ветки master Джессики, и она может
без проблем отправить свою работу на сервер при условии, что Джон за это время
не успел ничего добавить (рис. 5.11):
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master

Содействие проекту   141
issue54

4b078

1edee

fbff5

8149a

738ee

72bbc

23ac6

master

4af42

8059c

origin/master

Рис. 5.11. История Джессики после отправки внесенных ею изменений на сервер

Оба разработчика несколько раз фиксировали изменения и успешно выполняли
слияние своей работы с работой другого.
Это одна из простейших рабочих схем. Некоторое время вы работаете в тематической ветке, и когда приходит время, сливаете результаты своего труда в ветку
master. Решив, что пришло время поделиться своими наработками с коллегами,
вы скачиваете данные с сервера, и если там появились изменения, сливаете к себе
ветку origin/master, после чего содержимое ветки master можно отправить на
сервер. В общем виде эта последовательность соответствует рис. 5.12.
Джессика

Сервер

Джон
git clone

git clone

git commit

git commit
git push
git fetch
git merge
git push
git fetch
git merge
git push

Джессика

git fetch
Сервер

Джон

Рис. 5.12. Общая последовательность событий в простой схеме с несколькими
разработчиками

142    Глава 5 • Распределенная система Git

Маленькая группа с руководителем
Следующий сценарий демонстрирует роли участников проекта в закрытой группе
большего размера. Мы рассмотрим схему работы в среде, где небольшие группы занимаются отдельными задачами, а интеграцию результатов их труда осуществляет
третья сторона.
Предположим, что над одной задачей работают Джон и Джессика, а над другой
Джессика и Джози. В фирме есть также должность диспетчера интеграции, то
есть интеграцией результатов труда отдельных групп занимаются специальные
люди, имеющие право обновлять ветку master главного репозитория. При таком
сценарии вся работа выполняется в ветках отдельных команд, а ее объединение
осуществляется позднее.
Рассмотрим рабочий процесс занятой в решении двух задач Джессики, которая
при этом сотрудничает с двумя разными разработчиками. Будем считать, что у
нее уже есть собственная копия репозитория, и первым делом Джессика занялась
задачей featureA. Для этой задачи создается новая ветка, в которой производятся
некие действия:
# Компьютер Джессики
$ git checkout -b featureA
Switched to a new branch "featureA"
$ vim lib/simplegit.rb
$ git commit -am 'add limit to log function'
[featureA 3300904] add limit to log function
1 files changed, 1 insertions(+), 1 deletions(-)

Теперь ей нужно поделиться своей работой с Джоном, поэтому коммиты из ветки
featureA требуется отослать на сервер. Но у Джессики нет прав на запись в ветку
master — этим занимается диспетчер интеграции, — поэтому для совместной работы
с Джоном она отправляет свои данные в другую ветку:
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA

По электронной почте она сообщает Джону, что он может ознакомиться с выложенной в ветку featureA информацией. Ожидая реакции Джона, Джессика решает
приступить к работе в ветке featureB вместе с Джози. Для начала следует создать
эту ветку, используя в качестве основы ветку master с сервера:
# Компьютер Джессики
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'

После этого Джессика фиксирует в ветке featureB несколько изменений:
$ vim lib/simplegit.rb
$ git commit -am 'made the ls-tree function recursive'

Содействие проекту   143
[featureB e5b0fdc] made the ls-tree function recursive
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'add ls-files'
[featureB 8512791] add ls-files
1 files changed, 5 insertions(+), 0 deletions(-)

После этого репозиторий Джессики выглядит так, как показано на рис. 5.13.

4b078

master

feature A

1edee

33009

e5b0f

85127

feature B

Рис. 5.13. Исходная история коммитов Джессики

Готовясь выложить свою работу в общий доступ, Джессика получает от Джози
сообщение, информирующее о наработках, уже выложенных на сервер в ветку
featureBee. Значит, для получения возможности отправки данных на сервер ей
следует слить эти изменения со своими. Коммит Джози извлекается командой
git fetch:
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee

Теперь можно воспользоваться командой git merge и добавить эти изменения
к результатам своего труда:
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by recursive.
lib/simplegit.rb | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)

Но есть небольшая проблема. Полученные после слияния данные из ветки featureB
Джессике нужно выложить в ветку featureBee на сервере. Это делается командой git
push с указанием разделенных двоеточием названий локальной и удаленной веток:

144    Глава 5 • Распределенная система Git
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee

Такая запись называется спецификацией ссылок (refspec). Заодно обратите внимание на флаг -u, представляющий собой краткое обозначение параметра --setupstream, который устанавливает конфигурацию ветки, облегчающую дальнейшую
отправку данных на сервер и скачивание их оттуда.
Внезапно Джессика получает сообщение от Джона. Он информирует, что добавил
в ветку featureA кое-какие, изменения и просит проверить их. Джессика скачивает
эти изменения командой git fetch:
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA

Затем с помощью команды git log она смотрит, что именно изменилось:
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith
Date:
Fri May 29 19:57:33 2009 -0700
changed log output to 30 from 25

После этого она сливает работу Джона в свою ветку featureA:
$ git checkout featureA
Switched to branch "featureA"
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
lib/simplegit.rb | 10 +++++++++1 files changed, 9 insertions(+), 1 deletions(-)

Джессика хочет внести кое-какие дополнения, после чего она фиксирует изменения
и снова отправляет коммит на сервер:
$ git commit -am 'small tweak'
[featureA ed774b3] small tweak
1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
3300904..ed774b3 featureA -> featureA

После этого ее история коммитов выглядит так, как показано на рис. 5.14.
Джессика, Джози и Джон информируют человека, отвечающего за интеграцию,
о том, что ветки featureA и featureBee на сервере готовы к добавлению в основную ветку разработки. После завершения интеграции скачивание данных с сервера
приведет к появлению новых коммитов слияния (рис. 5.15).

Содействие проекту   145
origin/
feature A
master

4b078

1edee

feature A

33009

aad88

774b3

e5b0f

85127

cd685

fba9a

feature B
origin/
feature B

Рис. 5.14. История коммитов Джессики после фиксации изменений в ветке
с решаемой задачей

origin/
feature A
master

4b078

1edee

feature A

33009

e5b0f

fba9a

aad88

774b3

origin/master

5399e

85127

cd685

feature B
origin/
feature B

Рис. 5.15. История Джессики после слияния обеих ее тематических веток

146    Глава 5 • Распределенная система Git
Многие группы переходят на систему Git именно ради разработок в параллельном
режиме с последующим их объединением. Возможность организовать работу небольших групп в удаленных ветках, не мешая при этом всей команде, является
огромным преимуществом Git. Последовательность действий в описанной схеме
работы представлена на рис. 5.16.
Джессика
git commit
(A)

Джози

Сервер:
feature A

Джон

Сервер:
feature Bee

git push origin feature A
git commit
(A)

git fetch origin

git push origin feature A
git commit
(B)

git push origin feature Bee

git fetch origin feature B: featureBee
git merge
(B)

git fetch origin

git merge
(A)
git commit
(A)
git push origin feature A

Джессика

Джози

Джон

Сервер:
feature A

Сервер:
feature Bee

Рис. 5.16. Базовая последовательность действий при наличии
диспетчера интеграции

Открытый проект, ветвление
Вклад в открытый проект — это совсем другое дело. Так как права на прямое обновление веток проекта у вас отсутствуют, нужно каким-то способом организовать

Содействие проекту   147

работу с лицами, отвечающими за его поддержку. Сначала мы рассмотрим участие
в проекте посредством ветвлений. Многие сайты, предоставляющие хостинг для Git
(в том числе GitHub, BitBucket, Google Code, repo.or.cz), дают такую возможность,
да и владельцы проектов, ожидающие именно такой формы взаимодействия, тоже
весьма многочисленны. Следующий же раздел посвящен проектам, владельцы
которых предпочитают принимать исправления по электронной почте.
Первым делом требуется клонировать основной репозиторий, создать тематическую
ветку для задачи или набора задач, которые вы собираетесь решать, и приступить
к работе. Последовательность ваших действий будет следующей:
$
$
$
#
$
#
$

git clone (url)
cd project
git checkout -b featureA
(выполнение работы)
git commit
(выполнение работы)
git commit

ПРИМЕЧАНИЕ
Команда rebase -i позволяет объединить все наработки в один коммит или реорганизовать их внутри коммита таким образом, чтобы владельцу проекта было проще
с ними знакомиться.

Когда вы завершите работу с веткой и решите поделиться результатами с владельцами проекта, перейдите на главную страницу проекта и щелкните на кнопке Fork,
чтобы получить собственную копию проекта с правом на запись. URL-адрес этого
нового репозитория нужно добавить в список удаленных репозиториев. Мы дадим
ему имя myfork:
$ git remote add myfork (url)

Теперь следует отправить в этот репозиторий ваши наработки. Проще всего сразу
отправить ветку, в которой вы работаете, а не сливать ее предварительно с веткой
master. Это избавит вас от необходимости откатывать изменения ветки master,
если вашу работу не примут или примут только частично. Если владельцы проекта
выполнят слияние, перемещение или частичное включение вашей работы в основной код, вы в конечном счете все равно получите все обратно, скачав изменения из
главного репозитория:
$ git push -u myfork featureA

После отправки наработок в свою копию следует послать уведомление лицу,
отвечающему за поддержку проекта. Часто эту процедуру называют запросом
на включение (pull request). Запрос можно сгенерировать на сайте: например,
у GitHub есть собственный механизм таких запросов, который рассматривается в следующей главе, — или воспользоваться командой git request-pull

148    Глава 5 • Распределенная система Git
и по электронной почте отправить результат ее применения ответственному за
поддержку проекта.
В качестве аргумента команда request-pull принимает имя базовой ветки, в которую вы хотите включить свою тематическую ветку, и URL-адрес Git-репозитория, из
которого диспетчер интеграции может взять ваши наработки. Результатом работы
команды становится краткая сводка изменений, которые вы попросили включить
в проект. К примеру, если Джессика после пары коммитов в тематической ветке и
отправки этой ветки на сервер хочет послать Джону запрос на включение, ей нужно
сделать следующее:
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
John Smith (1):
added a new function
are available in the git repository at:
git://githost/simplegit.git featureA
Jessica Smith (2):
add limit to log function
change log output to 30 from 25
lib/simplegit.rb | 10 +++++++++1 files changed, 9 insertions(+), 1 deletions(-)

Вывод этой команды можно отправить диспетчеру интеграции — в нем сообщается,
где начинается ветка с изменениями, кратко отображаются все коммиты и указывается, откуда можно забрать изменения.
Для проекта, поддержкой которого занимаются другие люди, в общем случае имеет смысл завести ветку master, непрерывно следящую за веткой origin/master,
а всю работу выполнять в тематических ветках, которые легко можно удалить, если
ваши коммиты не будут приняты. Наличие тематических веток для отдельных
задач также облегчает процедуру перемещения работы в случае, когда за время ее
выполнения верхняя точка главного репозитория сместилась и применение коммитов без конфликтов стало невозможным. Например, если вы хотите добавить
к проекту работу по другой теме, не нужно продолжать работу над тематической
веткой, содержимое которой вы только что отправили на сервер. Начните с ветки
master главного репозитория:
$
#
$
$
#
$

git checkout -b featureB origin/master
(выполнение работы)
git commit
git push myfork featureB
(письмо диспетчеру интеграции)
git fetch origin

Теперь каждая тема будет находиться внутри собственного контейнера (похоже на
очередь из исправлений), которые вы можете перезаписывать, перемещать и редактировать, не затрагивая остальных тем (рис. 5.17).

Содействие проекту   149

4b078

master

origin/
master

1edee

33009

a2de3

feature A

0d708

feature B

e5b0f

Рис. 5.17. Исходная история коммитов с веткой featureB

Предположим, диспетчер интеграции скачал набор чьих-то исправлений, и после
этого оказалось, что ваша первая ветка не допускает бесконфликтного слияния.
Вы можете попробовать переместить эту ветку на вершину ветки origin/master,
разрешить конфликт и повторно отправить свои изменения:
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA

История коммитов при этом изменится (рис. 5.18).

4b078

master

origin/master

1edee

33009

feature A

5399e

dee6b

e5b0f

feature B

Рис. 5.18. История коммитов после работы в ветке featureA

Так как ветка была перемещена, чтобы заместить ветку featureA на сервере коммитом, который не является ее потомком, нужно воспользоваться командой push с
параметром -f. В качестве альтернативы эти наработки можно выложить в другую
ветку на сервере (назвав ее, к примеру, featureAv2).

150    Глава 5 • Распределенная система Git
Однако возможен и другой сценарий: диспетчер интеграции ознакомился с вашей
работой на второй ветке и одобрил идею, но хотел бы поменять некоторые детали
реализации. Для вас это возможность переместить свою работу таким образом,
чтобы она базировалась в текущей ветке master проекта. Вы создаете новую ветку
на базе ветки origin/master, вставляете туда изменения из ветки featureB, разрешаете все конфликты, вносите требуемые изменения в реализацию и отправляете
это все на сервер как новую ветку:
$
$
#
$
$

git checkout -b featureBv2 origin/master^{}
git merge --no-commit --squash featureB
(изменение реализации)
git commit
git push myfork featureBv2

Параметр --squash превращает все содержимое сливаемой ветки в один коммит,
не являющийся коммитом слияния, и помещает его на вершину текущей ветки.
Параметр --no-commit отменяет автоматическую запись коммита. В этом случае
вы сможете перед записью нового коммита добавить туда все изменения из другой
ветки и внести прочие коррективы (рис. 5.19).

4b078

master

origin/master

1edee

33009

17f4d

feature Bv2

dee6b

5399e

feature A

e5b0f

feature B

Рис. 5.19. История коммитов после работы на ветке featureBv2

Теперь диспетчеру интеграции можно отправить сообщение, информирующее о том,
что все требуемые изменения сделаны и находятся в ветке featureBv2.

Открытый проект, электронная почта
Во многих проектах существуют установленные процедуры приема изменений.
Точные правила каждый раз нужно выяснять отдельно. Тем не менее в некоторых
старых больших проектах изменения принимаются именно через списки рассылки
для разработчиков, поэтому мы рассмотрим этот вариант на примере.

Содействие проекту   151

Отличие от предыдущего случая состоит в способе внесения изменений. Вместо
создания собственной копии с правом на запись в случае каждого набора коммитов
генерируется версия для электронной почты с целью отправки в список рассылки
разработчиков:
$
#
$
#
$

git checkout -b topicA
(выполнение работы)
git commit
(выполнение работы)
git commit

Итак, у нас есть два готовых к отправлению коммита. Команда git format-patch
превратит их в сообщения электронной почты. Темой послужит первая строка сообщения фиксации, остальная часть этого сообщения и исправления войдут в тело
письма. Причем в изменениях из сгенерированного командой format-patch письма
корректно сохраняется вся информация о коммите.
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch

Команда format-patch выводит имена создаваемых ею файлов с исправлениями.
Параметр -M заставляет Git следить за переименованиями файлов. В итоге каждый
файл приобретает примерно вот такой вид:
$ cat 0001-add-limit-to-log-function.patch
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function
Limit log functionality to the first 20
--lib/simplegit.rb | 2 +1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
end
+

def log(treeish = 'master')
command("git log #{treeish}")
command("git log -n 20 #{treeish}")
end

def ls_tree(treeish = 'master')
-2.1.0

Эти файлы с исправлениями можно отредактировать, добавив к электронному
письму еще какую-то информацию, которую вы не захотели включать в сообщение

152    Глава 5 • Распределенная система Git
фиксации. Добавленный между строкой --- и началом исправления (строка diff
--git) текст сможет прочитать разработчик, но после примененияисправлений
он исчезнет.
Для отправки в список рассылки файл нужно либо вставить в почтовый клиент,
либо воспользоваться специальной программой, осуществляющей отправку из
командной строки. В первом случае зачастую возникают ошибки форматирования, особенно в «интеллектуальных» клиентах, не сохраняющих символы новой
строки и пробелы. К счастью, Git предоставляет инструмент для корректной отправки отформатированных изменений через IMAP. Мы также познакомимся с
отправкой изменений через службу Gmail, так как этот агент является, наверное,
самым распространенным; подробные инструкции для разных почтовых программ
вы найдете в уже упоминавшемся тут файле Documentation/SubmittingPatches,
входящем в исходный Git-код.
Первым делом нужно настроить раздел imap в файле ~/.gitconfig. Значения можно добавлять как по одному набором команд git config, так и вручную. В итоге
конфигурационный файл должен принять следующий вид:
[imap]
folder = «[Gmail]/Drafts»
host = imaps://imap.gmail.com
user = user@gmail.com
pass = p4ssw0rd
port = 993
sslverify = false

В случае IMAP-серверов, не работающих с протоколом SSL, последние две строки
не требуются, а параметр host должен иметь значение imap:// вместо imaps://.
После завершения настроек набор исправлений следует поместить в папку Drafts
на указанном IMAP-сервере командой git send-email:
$ git send-email *.patch
0001-added-limit-to-log-function.patch
0002-changed-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith ]
Emails will be sent from: Jessica Smith
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y

После этого Git добавит множество служебной информации, которая для каждого
отсылаемого исправления будет иметь вот такой вид:
(mbox) Adding cc: Jessica Smith from
\line 'From: Jessica Smith '
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith
To: jessica@example.com
Subject: [PATCH 1/2] added limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id:

Сопровождение проекта   153
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To:
References:
Result: OK

Теперь вы можете зайти в свою папку Drafts, ввести в поле To адрес списка рассылки и, возможно, добавить в поле CC адрес человека, отвечающего за поддержку
той части проекта, к которой относятся ваши изменения. После этого остается
только отправить письмо.

Заключение
Мы рассмотрели ряд общепринятых рабочих схем, применяемых для разных типов
Git-проектов, с которыми вам наверняка предстоит столкнуться. Вы познакомились
с несколькими новыми инструментами, помогающими в организации рабочего
процесса. Теперь мы рассмотрим ситуацию с другой стороны: как осуществляется
сопровождение Git-проекта? Вы узнаете, что следует делать на месте диктатора
или диспетчера интеграции.

Сопровождение проекта
Вы должны уметь не только результативно вносить свой вклад в проекты, но и поддерживать их. Сопровождение проекта может включать в себя как прием и применение исправлений, сгенерированных командой format-patch и присланных
вам по электронной почте, так и интеграцию изменений из веток тех репозиториев,
которые вы добавили к своему проекту в качестве удаленных. Не важно, отвечаете
вы за поддержку эталонного репозитория или просто хотите помочь с проверкой и
одобрением исправлений. В любом случае вам следует выработать метод приема
присылаемых данных, максимально прозрачный для остальных участников проекта
и подходящий вам в долгосрочной перспективе.

Работа с тематическими ветками
Новые наработки, предназначенные для интеграции, как правило, желательно
протестировать, поместив во временную специально созданную для этой цели
ветку. Это упрощает редактирование отдельных изменений и позволяет оставить
их до лучших времен, если выяснится, что что-то не работает. Простое, легко запоминающееся имя, связанное с темой содержащейся в ветке работы, например
ruby_client или что-то столь же наглядное, позволяет без проблем вспомнить
назначение ветки, если вам пришлось на время прекратить работу с ней. Человек,

154    Глава 5 • Распределенная система Git
отвечающий за поддержку Git-проекта, как правило, добавляет к этим именам название пространства имен, например sc/ruby_client, где sc — инициалы того, кто
добавил это исправление. Как вы помните, создать ветку на основе вашей ветки
master можно следующим образом:
$ git branch sc/ruby_client master

Мгновенно перейти в создаваемую ветку позволяет команда checkout -b:
$ git checkout -b sc/ruby_client master

Теперь в эту тематическую ветку можно принимать присылаемые изменения и смотреть, имеет ли смысл сливать их в свои стабильные ветки.

Исправления, присланные по почте
Полученное по электронной почте исправление первым делом следует поместить
в тематическую ветку, чтобы оценить его. Это можно сделать как командой git
apply, так и командой git am.

Команда apply
Если вы получили коммит, сгенерированный командой git diff или Unix-командой
diff (что, как вы вскоре убедитесь, не рекомендуется), его можно применить при
помощи команды git apply. Вот как выглядит эта процедура после сохранения
присланного исправления по адресу /tmp/patch-ruby-client.patch:
$ git apply /tmp/patch-ruby-client.patch

Эта команда меняет файлы в вашей рабочей папке. Она практически идентична
команде patch -p1 , но более подозрительна и допускает меньше нечетких совпадений. Кроме того, она осуществляет добавление, удаление и переименование
файлов, если это задано при форматировании командой git diff, чего команда
patch делать не умеет. Наконец, команда git apply работает по принципу «все или
ничего», в то время как команда patch допускает частичное принятие изменений,
оставляя рабочую папку в странном состоянии. В целом команда git apply куда
более консервативна. Она не создает коммиты — после нее следует вручную осуществить индексацию и зафиксировать изменения.
Еще команда git apply позволяет заранее узнать, аккуратно ли применяется исправление. Для этого нужно запустить ее с параметром --check, указав нужный файл:
$ git apply --check 0001-seeing-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply

Отсутствие вывода в данном случае означает отсутствие проблем в процессе применения исправления. В случае неудачного результата проверки команда завершается
с ненулевым статусом, что позволяет использовать ее в сценариях.

Сопровождение проекта   155

Команда am
Если исправление прислано опытным Git-пользователем, который сгенерировал
его командой format-patch, ваша задача упрощается, ведь этот файл содержит
сведения об авторе и сообщение фиксации. По возможности рекомендуйте участникам проекта пользоваться командой format-patch, а не командой diff. Команду
git apply нужно оставить для устаревших исправлений и таких вещей, о которых
мы рассказали.
Исправления, сгенерированные командой format-patch, применяются командой
git am. С технической точки зрения она просто читает mbox-файл, в котором в
виде обычного текста хранится одно или несколько электронных писем. Этот файл
имеет следующий вид:
From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] add limit to log function
Limit log functionality to the first 20

Это начало уже знакомого вам по предыдущему разделу вывода команды formatpatch. Кроме того, это корректный формат mbox для электронной почты. Если
кто-то правильно воспользовался командой git send-email и прислал вам исправление, которое вы сохранили в формате mbox, сохраненный файл можно передать команде git am, и она начнет применять все обнаруженные ею исправления.
Если ваш почтовый клиент в состоянии сохранить несколько электронных писем
в одном mbox-файле, то команда git am, которой вы передадите этот файл, начнет
применять их по очереди.
Однако если исправление, сгенерированное командой format-patch, было загружено в систему отслеживания ошибок или нечто подобное, файл можно сохранить
локально и уже после этого передать его команде git am:
$ git am 0001-limit-log-function.patch
Applying: add limit to log function

Как видите, исправление было наложено без ошибок, и для вас автоматически
создали новый коммит. Сведения об авторе взяты из полей From и Date, а сообщение
коммита — из поля Subject и тела письма (до начала исправления). Скажем, если применить исправление из представленного примера, получится следующий коммит:
$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author: Jessica Smith
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit: Scott Chacon
CommitDate: Thu Apr 9 09:19:06 2009 -0700
add limit to log function
Limit log functionality to the first 20

156    Глава 5 • Распределенная система Git
В поле Commit указаны человек, применивший исправление, и дата этого со­
бытия. Поле Author содержит информацию о том, кто создал исправление, и дату
создания.
Однако применение исправлений далеко не всегда происходит гладко. Скажем,
может возникнуть ситуация, когда основная ветка слишком расходится с веткой,
послужившей основой для исправления, или, к примеру, исправление зависит от
другого исправления, которое пока не применили. В этом случае выполнение коман­
ды git am прекращается, а вы получаете запрос, как следует поступить:
$ git am 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

Эта команда ставит метки о конфликтах на все файлы, с которыми возникает проблема, аналогично тому, как это происходит при конфликтах слияния или перемещения. Сходен и способ решения этой проблемы: вы редактируете файл, разрешая конфликт, индексируете получившуюся новую версию и запускаете команду
git am --resolved, чтобы перейти к следующему исправлению:
$ (исправление файла)
$ git add ticgit.gemspec
$ git am --resolved
Applying: seeing if this helps the gem

Можно заставить Git прибегнуть к более интеллектуальному решению конфликта,
добавив к команде параметр -3 и инициировав трехэтапное слияние. По умолчанию этот параметр отсутствует, так как если в вашем репозитории нет коммита,
послужившего основой для исправления, команда работать не будет. При наличии
же этого коммита, например если исправление основано на открытом коммите,
параметр -3 в общем случае помогает действовать более интеллектуально в плане
применения конфликтующего исправления:
$ git am -3 0001-seeing-if-this-helps-the-gem.patch
Applying: seeing if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.

В данном случае рассматриваемое исправление уже было применено. Но без параметра -3 оно имело бы вид конфликтующего.
При применении серии исправлений из mbox-файла команду am можно запустить
в интерактивном режиме. В этом случае после обнаружения очередного исправления
будет появляться запрос о необходимости его применения:

Сопровождение проекта   157
$ git am -3 -i mbox
Commit Body is:
-------------------------seeing if this helps the gem
-------------------------Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all

Это удобно при работе с большим количеством исправлений, так как если вы забыли, что представляет собой конкретное исправление, на него сначала можно
посмотреть, кроме того, можно отказаться от применения уже сделанных ранее
исправлений.
После применения всех исправлений по выбранной теме и слияния их с вашей
веткой можно принимать решение об их интеграции в стабильную ветку.

Проверка удаленных веток
Если вы получили наработки от Git-пользователя, настроившего свой собственный
репозиторий, отправившего туда ряд изменений и приславшего вам его URL-адрес
и имя удаленной ветки с изменениями, этот репозиторий можно добавить в качестве
удаленного и выполнять все слияния локально.
К примеру, предположим, вы получаете от Джессики сообщение о том, что у нее
в ветке ruby-client появился классный новый программный компонент. Протестировать этот компонент можно, добавив репозиторий Джессики в качестве
удаленного и проверив указанную ветку локально:
$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

Получив от Джессики еще одно письмо с другой веткой и новым замечательным
программным компонентом, вы можете сразу извлечь все наработки и перейти
в указанную ветку, так как ее хранилище уже фигурирует у вас как удаленное.
Этот метод особенно эффективен, когда вы работаете с человеком на постоянной
основе. Если пользователь изредка присылает вам какие-то исправления, проще
принять их по электронной почте, чем требовать от него поддержки собственного
сервера и постоянно добавлять и ликвидировать удаленные ветки. Вряд ли вы
захотите держать у себя сотни удаленных репозиториев, принадлежащих пользователям, которые когда-то прислали вам исправление-другое. Ситуация слегка
упрощается в случае сценариев и служб при использовании чужого хостинга, но
опять же все зависит от способа ведения разработки вами и остальными участниками проекта.
Другим достоинством данного подхода является получение вместе с исправлениями
истории коммитов. Даже если вы унаследуете проблемы со слиянием, всегда можно
будет узнать, где кроется их причина; в данном случае по умолчанию используется
корректное трехэтапное слияние вместо добавления параметра -3 в надежде, что

158    Глава 5 • Распределенная система Git
исправление было сгенерировано на основе открытого коммита, который вы сможете посмотреть.
Если же вы хотите принять таким способом изменения от человека, с которым
вы не работаете на постоянной основе, можно передать URL-адрес его удален­
ного репозитория команде git pull . Это даст возможность выполнить однократное скачивание данных без сохранения URL-адреса в списке удаленных
репозиториев:
$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
* branch HEAD -> FETCH_HEAD
Merge made by recursive.

Просмотр вносимых изменений
Итак, у вас есть тематическая ветка с наработками участников проекта. Пришла
пора решать, что с ними делать. В этом разделе вы заново встретите знакомые
­команды, но на этот раз они помогут точно определить, к каким результатам приведет включение в вашу основную ветку присланных изменений.
Зачастую имеет смысл изучить все коммиты, присутствующие в этой ветке, но отсутствующие в вашей ветке master.
Убрать из списка коммиты, которые уже есть в ветке master, позволит параметр
--not, добавляемый перед именем ветки. Он делает то же самое, что и знакомая вам
запись master..contrib. К примеру, если участник проекта прислал два изменения,
а вы создали ветку contrib и применили их там, можно написать:
$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon
Date: Fri Oct 24 09:53:59 2008 -0700
seeing if this helps the gem
commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon
Date: Mon Oct 22 19:38:36 2008 -0700
updated the gemspec to hopefully work better

Чтобы увидеть, к каким изменениям приведет каждый коммит, достаточно добавить
к команде git log параметр –p. После этого к каждому коммиту будет добавлен
фрагмент с нововведениями.
Чтобы понять, что случится после слияния тематической ветки с другой веткой,
потребуется странный трюк. Дело в том, что следующая команда выводит перечень
внесенных изменений, но этот результат далеко не всегда является корректным:
$ git diff master

Сопровождение проекта   159

К примеру, если с момента создания тематической ветки ветка master была перемотана вперед, результат окажется непонятным. Дело в том, что Git напрямую сравнивает снимок последнего коммита тематической ветки, в которой вы находитесь,
со снимком последнего коммита ветки master. Скажем, если вы добавили строку
в файл из ветки master, прямое сравнение состояний покажет, что в тематической
ветке эту строку собираются удалить.
Если ветка master является прямым предком вашей тематической ветки, проблем
нет; но если история в какой-то точке расходится, выводимые изменения будут
иметь такой вид, как будто все новое вы добавляете в тематическую ветку, а все
уникальное удаляете из ветки master.
Нам же всего лишь требуется посмотреть изменения, добавленные в тематическую
ветку — то есть данные, вносимые путем слияния в ветку master. Для этого нужно
заставить Git сравнить последний коммит тематической ветки с первым предком,
который будет у него общим с веткой master.
Технически можно вручную определить такого предка и выполнить команду diff:
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

Но это не очень удобно, поэтому в Git для этого случая предусмотрена особая
нотация: запись с тремя точками. Если в контексте команды diff поставить после
имени одной из веток три точки, вы увидите разницу между последним коммитом
ветки, в которой вы находитесь, и ее общим предком с другой веткой:
$ git diff master...contrib

В такой форме команда выводит вам только те наработки из тематической ветки,
которые появились там после расхождения с веткой master. Это очень удобный
синтаксис, так что его имеет смысл запомнить.

Интеграция чужих наработок
В конце концов наступает момент, когда все наработки в тематической ветке готовы
к интеграции в более стабильную ветку. Остается вопрос, как это сделать. Какую
схему работы вы предпочтете для поддержки своего проекта? Существует множество вариантов, некоторые из них рассмотрены в данном разделе.

Схемы слияния
Одна из простых рабочих схем состоит в добавлении наработок в вашу ветку master.
В этом сценарии ветка master содержит основную стабильную версию кода. Если
на тематической ветке находится сделанная вами или присланная кем-то и проверенная вами работа, содержимое этой ветки сливают в ветку master, затем ветку

160    Глава 5 • Распределенная система Git
удаляют и продолжают процесс. Если в вашем репозитории находятся наработки
из двух веток ruby_client и php_client, имеющие такой же вид, как показано на
рис. 5.20, и вы выполняете сначала слияние ветки ruby_client, а потом — ветки
php_client, история будет выглядеть в соответствии с рис. 5.21.
master

C1

C2

C7

C3

C4

ruby_client

C5

C6

php_client

Рис. 5.20. История коммитов с несколькими тематическими ветками

master

C1

C2

C7

C8

C3

C4

C5

C6

C9

ruby_client

php_client

Рис. 5.21. Вид после слияния тематических веток

Это, возможно, самая простая рабочая схема, но в случае более крупных или более
стабильных проектов, когда нужно очень аккуратно подходить к добавлению изменений, она может привести к проблемам.
Для важных проектов имеет смысл пользоваться двухэтапным циклом слияний.
В этом случае у вас будут две долгоживущие ветки master и develop, при этом
первая ветка обновляется только при добавлении крайне стабильного кода, а весь
новый код попадает в ветку develop. Содержимое этих веток регулярно отправляется в открытый репозиторий. При каждом появлении новой тематической ветки

Сопровождение проекта   161

для слияния (рис. 5.22) ее содержимое сливают в ветку develop (рис. 5.23). Затем,
когда вы помечаете новую версию тегом, ветка master перематывается до стабильного коммита ветки develop (рис. 5.24).

C1

master

develop

C2

C5

C3

ruby_client

C4

Рис. 5.22. До слияния с тематической веткой

master

C1

C2

develop

C5

C3

C6
ruby_client

C4

Рис. 5.23. После слияния с тематической веткой

master

C1

C2

C5

C3

develop

C6

C4

ruby_client

Рис. 5.24. После выхода версии проекта

В этом случае клонирующие ваш репозиторий пользователи могут либо получить
последнюю стабильную версию из ветки master и легко поддерживать актуальность
кода, либо перейти в ветку develop, содержащую новейшие обновления. Данный
подход можно развить, добавив ветку для интеграции, в которой будет осуществляться слияние всех наработок. А когда код этой ветки станет стабильным и пройдет тестирование, его можно будет слить в ветку develop; если в течение некоторого
времени он будет работать нормально, вы выполните перемотку ветки master.

162    Глава 5 • Распределенная система Git

Схема с большим количеством слияний
В Git-проекте есть четыре долгоживущие ветки: master, next и pu (последняя
расшифровывается как proposed updates — предложенные обновления) для новых наработок и maint для поддержки совместимости с более старыми версиями.
Предложенные участниками проекта наработки накапливаются в тематических
ветках в репозитории отвечающего за поддержку проекта лица описанным ранее
способом. На этом этапе производится оценка содержимого тематических веток,
чтобы определить, работают ли предложенные фрагменты так, как положено, или
им требуется доработка. Если все в порядке, тематические ветки сливаются в ветку
next, которая отправляется на сервер, чтобы у каждого была возможность опробовать результат интеграции (рис. 5.25).
master

C1

C2

C3

C4

tv/rebase-stat

C5

C6

jk/clone-checkout

C7

C8

db/push-cleanup

C9

C10

js/notes

C11

C12

ps/blame

Рис. 5.25. Управление группой параллельных тематических веток участников
проекта

Если содержимое тематических веток требует доработки, оно сливается в ветку pu.
Когда выясняется, что предложенный код полностью стабилен, он сливается в ветку
master и перестраивается с учетом данных из ветки next, пока еще не попавших в
ветку master. Это означает, что ветка master почти всегда движется вперед, ветка
next периодически подвергается перемещениям, а содержимое ветки pu перемещается еще чаще (рис. 5.26).
Когда, наконец, тематическая ветка полностью сливается в ветку master , она
удаляется из репозитория. В проекте Git также присутствует ветка maint, которая является ответвлением от последней выпущенной версии, предназначенным

Сопровождение проекта   163

для исправлений более старой версии. В результате после клонирования Gitрепозитория у вас оказывается четыре ветки, переключаясь между которыми вы
можете оценить проект на разных стадиях разработки в зависимости от того, какую
версию вы хотите получить или каким образом собираетесь сделать вклад в проект.
Лицо, отвечающее за поддержку проекта, при этом действует в рамках структурированного рабочего процесса, помогающего ему изучать новые присланные изменения.

C9

C10

js/notes

C11

C12

ps/blame

master

C1

C16

C17

pu

C14

C15

next

C2
C13

C3

C4

tv/rebase-stat

C5

C6

jk/clone-checkout

C7

C8

db/push-cleanup

Рис. 5.26. Слияние тематических веток участников проекта в долгоживущие
интегрированные ветки

Схема с перемещением и отбором
Некоторые владельцы проектов предпочитают выполнять не слияние, а перемещение или отбор лучших наработок в ветку master, чтобы получить практически
линейную историю. Если у вас есть наработки в тематической ветке, которые вы
хотите интегрировать в проект, вы заходите в эту ветку и запускаете команду rebase,
чтобы переместить все изменения на вершину текущей ветки master (или ветки
develop и т. п.). Если все проходит успешно, можно выполнить перемотку ветки
master, получив в итоге линейную историю проекта.
Другим способом перемещения предложенных наработок из одной ветки в другую
является отбор лучшего. В Git эта процедура сводится к перемещению отдельных

164    Глава 5 • Распределенная система Git
коммитов. Берутся представленные в коммите изменения и делается попытка применить их в вашей текущей ветке. Это удобно, если из набора коммитов в тематической ветке вы хотите интегрировать только один или если в этой ветке есть всего
один коммит, но вы предпочитаете произвести операцию отбора, а не перемещения.
Предположим, ваш проект выглядит так, как показано на рис. 5.27.
master

0b743

a6b4c

f42c5

e43a6

5ddae

ruby_client

Рис. 5.27. Пример истории, из которой нужно отобрать лучшее

Чтобы поместить коммит e43a6 в свою ветку master, выполните команду:
$ git cherry-pick e43a6fd3e94888d76779ad79fb568ed180e5fcdf
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
3 files changed, 17 insertions(+), 3 deletions(-)

Эта команда извлечет изменения, появившиеся в коммите e43a6, но при этом
изменится контрольная сумма SHA-1 коммита, так как у него будет другая дата
применения (рис. 5.28).
master

0b743

a6b4c

f42c5

a0a41

e43a6

5ddae

ruby_client

Рис. 5.28. История после отбора лучшего коммита из тематической ветки

Сопровождение проекта   165

Теперь тематическую ветку можно удалить, отбросив коммиты, которые вы не собираетесь включать в проект.

Программный компонент rerere
Для тех, кто часто выполняет слияния и перемещения или поддерживает долгоживущие ветки, в Git есть вспомогательный программный компонент rerere.
Как следует из названия, которое представляет собой аббревиатуру фразы «reuse
recorded resolution» (повторное использование записанного решения), он дает возможность сократить процедуру ручного разрешения конфликтов. После включения
этого программного компонента Git начинает сохранять набор пред- и постобразов
успешных слияний. Обнаружив, что текущий конфликт точно напоминает какойто из ранее решавшихся, система разрешает его аналогичным образом, не требуя
вашего участия в этом процессе.
Этот программный компонент представлен в двух формах: в виде конфигурационного параметра и команды. Параметр rerere.enabled настолько удобен, что имеет
смысл поместить его в global config:
$ git config --global rerere.enabled true

Теперь результат каждого слияния, сопровождающегося разрешением конфликтов,
будет записываться в кэш.
При необходимости этим кэшем можно воспользоваться при помощи команды git
rerere. После этой команды Git проверяет базу данных решений, пытаясь найти
там совпадение с текущим конфликтом слияния и решить его (впрочем, после присвоения параметру rerere.enabled значения true это делается автоматически).
Существуют и дополнительные варианты команды, позволяющие посмотреть,
что именно будет записываться, убрать отдельные решения из кэша или очистить
кэш целиком.

Идентификация устойчивых версий
Решив выпустить устойчивую версию, вы, скорее всего, захотите присвоить ей тег,
чтобы в будущем его можно было легко в любой момент восстановить. Если вы
предпочитаете создать новый тег как владелец версии, процедура будет выглядеть
примерно так:
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon "
1024-bit DSA key, ID F721C45A, created 2009-02-09

Если вы подписываете свои теги, может возникнуть проблема с распростра­нением используемого для этой цели открытого PGP-ключа. Лица, ответственные за поддержку

166    Глава 5 • Распределенная система Git
Git-проекта, решили эту проблему, добавив этот открытый ключ в репозиторий в
виде массива двоичных данных и установив тег, указывающий непосредственно на
эту информацию. Если вы хотите пойти по данному пути, ­первым делом определите,
какой ключ вам нужен, запустив команду gpg --list-keys:
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
--------------------------------pub 1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid
Scott Chacon
sub 2048g/45D02282 2009-02-09 [expires: 2010-02-09]

Выбранный ключ можно вставить непосредственно в базу данных Git-проекта,
экспортировав его и передав команде git hash-object, которая создаст из содержимого этого ключа новый массив двоичных данных и вернет вам его контрольную
сумму SHA-1:
$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92

Теперь, когда в системе Git хранится содержимое этого ключа, легко можно создать
указывающий на него тег, воспользовавшись данным вам командой git hash-object
значением контрольной суммы SHA-1:
$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Команда git push --tags делает тег maintainer-pgp-pub общедоступным. Для проверки тега любой пользователь может напрямую импортировать ваш PGP-ключ,
взяв информацию непосредственно из базы данных и вставив ее в программу GPG:
$ git show maintainer-pgp-pub | gpg --import

Этим ключом пользователи могут проверять все подписанные вами теги. Если в
сообщение тега добавлена инструкция, команда git show выдаст конечному
пользователю инструкцию по проверке тегов.

Генерация номера сборки
Поскольку равномерно возрастающие номера, такие как v123, в Git для коммитов
не используются, присвоить удобное для восприятия имя позволяет команда git
describe. Она возвращает имя ближайшего тега с количеством сделанных поверх этого тега коммитов и частичной контрольной суммой SHA-1 описываемого
коммита:
$ git describe master
v1.6.2-rc1-20-g8c5b85c

Это дает возможность присвоить снимку состояния проекта или сборке понятное
для пользователей имя. По сути, при сборке Git-кода из кода, клонированного из

Сопровождение проекта   167

Git-репозитория, нечто подобное возвращает команда git --version. При описании коммита, которому был в явном виде присвоен тег, команда возвращает имя
этого тега.
Команда git describe хорошо работает в случае тегов, снабженных комментариями (то есть созданных с флагом -a или -s), поэтому именно таким способом
нужно создавать теги для устойчивых версий. Ведь при помощи команды git
describe можно удостовериться в корректности имени тега. К этой строке также
можно ­применять команды checkout и show, но в будущем они могут перестать
рабо­тать из-за сокращенного значения SHA-1. Например, в ядре для обеспечения
уникальности объектов с недавнего времени требуется не в 8, а 10 символов ­SHA-1,
поэтому старые имена, сгенерированные командой git describe, стали недействительными.

Подготовка устойчивой версии
Итак, вы готовы выпустить свою сборку как основную версию. Возможно, вы захотите создать архив последнего состояния кода для тех пользователей, у которых
отсутствует система Git. Это делается командой git archive:
$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

Открывший этот tarball-архив пользователь получит последний снимок состояния
вашего проекта в папке project. Можно создать и zip-архив, добавив команде git
archive параметр --format=zip:
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

В итоге вы получите tarball- и zip-архивы с устойчивой версией вашего проекта,
которые можно загрузить на сайт или отправить пользователям по электронной
почте.

Команда shortlog
Чтобы отправить коллегам по своему списку рассылки сообщение с информацией
о последних новостях вашего проекта, используется команда git shortlog, позволяющая создать нечто вроде журнала, описывающего, что было добавлено к проекту после выхода последней версии или последней рассылки. Журнал включает
в себя все коммиты в указанном вами диапазоне; к примеру, следующая команда
возвращает перечень коммитов, сделанных с момента выхода версии v1.0.1:
$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (8):
Add support for annotated tags to Grit::Tag

168    Глава 5 • Распределенная система Git
Add packed-refs annotated tag support.
Add Grit::Commit#to_patch
Update version and History.txt
Remove stray `puts`
Make ls_tree ignore nils
Tom Preston-Werner (4):
fix dates in history
dynamic version method
Version bump to 1.0.2
Regenerated gemspec for version 1.0.2

То есть мы получаем аккуратную сводку всех коммитов, начиная с версии v1.0.1,
сгруппированных по авторам, которую можно разослать своим подписчикам.

Заключение
К этому моменту вы должны уметь не только вносить свой вклад в чужие Gitпроекты, но и поддерживать собственный проект, интегрируя в него изменения, присылаемые другими участниками. Фактически вас можно поздравить с вхождением
в клан опытных Git-разработчиков! В следующей главе вы научитесь пользоваться
самым крупным и самым популярным Git-хостингом, который называется GitHub.

6

GitHub

Сайт GitHub является крупнейшим на сегодняшний день хостингом для Gitрепозиториев и центральным местом сотрудничества миллионов разработчиков
и проектов. Именно на этом сайте располагается подавляющая часть всех Gitрепозиториев, а многие проекты с открытым исходным кодом используют его для
хостинга Git, отслеживания ошибок, рецензирования кода и других вещей. И хотя
этот сайт пока не является частью открытого Git-проекта, велики шансы, что когданибудь вам потребуется воспользоваться им в рабочих целях.
В этой главе мы поговорим о том, как добиться максимально эффективной работы
с GitHub. Вы узнаете, как создать и поддерживать свою учетную запись, научитесь
создавать и использовать Git-репозитории, познакомитесь с общепринятыми схемами внесения изменений в чужие проекты и принятия изменений, присланных в
ваш проект, освоите программный интерфейс для GitHub и получите множество
советов, позволяющих изрядно облегчить жизнь.
Если вы не собираетесь использовать GitHub для размещения собственных проектов или сотрудничать в проектах, использующих этот хостинг, можете сразу
перейти к чтению главы 7.
ИЗМЕНЕНИЯ ИНТЕРФЕЙСА
Следует заметить, что интерфейс сайта GitHub, как и интерфейс многих других активно развивающихся сайтов, со временем меняется, соответственно представленные
на снимках элементы пользовательского интерфейса на момент чтения данной книги
могут иметь немного другой вид. Но основной смысл выполняемых операций от этого
не меняется. Более актуальную версию снимков экрана в некоторых случаях можно
найти в онлайн-версии этой книги.

170    Глава 6 • GitHub

Настройка и конфигурирование учетной записи
Первым делом нужно создать бесплатную учетную запись. Для этого перейдите
на страницу https://github.com, выберите свободное имя пользователя, укажите свой
адрес электронной почты и пароль, после чего щелкните на большой зеленой кнопке
Sign up for GitHub (рис. 6.1).

Рис. 6.1. Регистрационная форма на сайте GitHub

После этого вы попадете на страницу с тарифными планами, которую на данном
этапе можно пропустить. Вам будет отправлено письмо для проверки указанного
при регистрации адреса электронной почты. Выполните присланную в письме
инструкцию. Как вы скоро убедитесь, это действительно необходимо.
ПРИМЕЧАНИЕ
Для бесплатных учетных записей сайт GitHub предоставляет всю функциональность,
но ваши проекты будут полностью открытыми (у любого пользователя будет доступ
на чтение). Платные учетные записи в GitHub дают возможность создавать различные
варианты закрытых проектов, но их рассмотрение выходит за рамки темы данной книги.

Щелчок на расположенном в верхнем левом углу экрана логотипе, изображающем
гибрид кота и осьминога (его называют осьмикот), откроет панель управления.
Теперь все готово для работы с GitHub.

Доступ по протоколу SSH
С этого момента вы можете подключаться к Git-репозиториям, используя протокол https:// protocol. Для аутентификации достаточно указать имя поль­
зователя и пароль. Впрочем, клонировать открытые проекты можно и без регистра­

Настройка и конфигурирование учетной записи   171

ции — созданная вами учетная запись пригодится, когда вам потребуется выполнить
ветвление какого-либо проекта и загрузить в новую ветку свои изменения.
Если же для удаленного доступа вы предпочитаете протокол SSH, нужно будет настроить открытый ключ. (Если
такого ключа у вас пока нет,
см. раздел «Создание открыРис. 6.2. Ссылка на страницу настройки
того ключа SSH» в главе 4.)
учетной записи
Перейдите к настройкам своей
учетной записи, воспользовавшись распложенной в правом верхнем углу окна ссылкой (рис. 6.2).
Затем выберите на левой панели раздел SSH Keys (рис. 6.3).

Рис. 6.3. Вход в раздел SSH keys

Щелкните на кнопке Add an SSH key, укажите имя ключа, скопируйте и вставьте сам
открытый ключ из файла ~/.ssh/id_rsa.pub (у вас этот файл может называться
по-другому) и щелкните на кнопке Add key.
ПРИМЕЧАНИЕ
Присваивайте SSH-ключам имена, которые вы в состоянии запомнить. Выбирайте
разные имена (например, «My Laptop» или «Work Account»), чтобы в будущем, если
вдруг возникнет необходимость убрать ключ, можно было безошибочно найти нужный.

172    Глава 6 • GitHub

Аватар
Теперь при желании можно заменить сгенерированный аватар собственным изображением. На левой панели выберите вкладку Profile и щелкните на кнопке Upload
new picture (рис. 6.4).

Рис. 6.4. Кнопка перехода на вкладку Profile

Мы выбрали на своем жестком диске копию логотипа Git. При загрузке из нее
можно вырезать произвольный фрагмент (рис. 6.5).

Рис. 6.5. Обрезка выбранного аватара

Настройка и конфигурирование учетной записи   173

Теперь при любых ваших действиях на сайте рядом с вашим именем пользователя
будет фигурировать выбранный аватар.
Если вы зарегистрированы в популярной службе Gravatar (она часто используется
для учетных записей в WordPress), по умолчанию будет отображаться аватар оттуда,
и данный этап вы можете просто пропустить.

Адреса электронной почты
GitHub проецирует ваши Git-коммиты на адрес электронной почты. Если в коммитах фигурируют разные адреса, все их нужно добавить в раздел Emails панели
управления, чтобы сайт GitHub корректно выполнял проецирование.
На рис. 6.6 показан перечень возможных ситуаций. Верхний адрес проверен и установлен в качестве основного (primary), то есть именно на него вы будете получать
все уведомления и подтверждения. Второй адрес проверен и поэтому тоже может
быть установлен в качестве основного, если вдруг у вас возникнет желание переключиться на него. Проверка последнего адреса пока не произведена, поэтому в
качестве основного он фигурировать не может. Обнаружив любой из этих адресов
в сообщениях фиксации в любом своем репозитории, GitHub сразу же свяжет их
с вашей учетной записью.

Рис. 6.6. Добавление адреса электронной почты

Аутентификация по двум признакам
Наконец, в качестве дополнительной меры безопасности можно настроить аутентификацию по двум признакам (Two-factor Authentication, 2FA). Этот механизм

174    Глава 6 • GitHub
в последнее время набирает все большую популярность как средство снизить риск
взлома учетной записи в случае, если ваш пароль каким-то образом попадет в руки
злоумышленника. После включения этого механизма GitHub начинает запрашивать
у вас аутентификацию двумя способами, в результате даже взлом пароля не дает
атакующему доступа к вашей учетной записи.
Аутентификация по двум признакам включается в разделе Security настроек учетной
записи (рис. 6.7).

Рис. 6.7. Включение режима 2FA на вкладке Security

Щелчок на кнопке Set up two-factor authentication открывает конфигурационную страницу, на которой можно выбрать мобильное приложение для генерации вторичного
кода проверки (однократный пароль с временным критерием). Кроме того, можно
сделать так, чтобы при попытке авторизации сайт GitHub отправлял вам SMS
с кодом.
После того как вы выберете метод и выполните настройку режима 2FA, безопасность
вашей учетной записи возрастет, и при переходе в нее вам потребуется вводить не
только пароль, но и дополнительный код.

Содействие проекту
Теперь, когда у нас есть настроенная учетная запись, поговорим о вещах, которые
могут пригодиться тем, кто хочет внести свой вклад в существующие проекты.

Содействие проекту   175

Ветвления проектов
Для участия в чужом проекте, где у вас нет права на запись, нужно создать ветку (fork) этого проекта. То есть GitHub генерирует отдельную копию проекта,
­на­ходящуюся в вашем пространстве имен, и предоставляет вам доступ на запись
туда.
ПРИМЕЧАНИЕ
Исторически термин ветвление (fork) имел негативный оттенок, так как означал, что
кто-то поменял направление проекта с открытым исходным кодом, например, создав
конкурирующий проект и переманив в него участников. Однако в контексте GitHub
этим термином обозначается клон проекта, находящийся в вашем пространстве имен
и позволяющий открыто вносить изменения в основную версию проекта.

Такой подход избавляет владельцев проектов от необходимости добавлять пользователей, желающих принять участие в работе, и предоставлять им доступ на запись. Любой человек может создать собственную ветку, внести туда изменения и
отправить их в исходный репозиторий путем запроса на включение. Такие запросы
подробно рассматриваются далее в этой главе. Они открывают цепочки обсуждения
с анализом кода, в которых владелец и участник проекта могут согласовывать изменения, пока они не устроят владельца и он не осуществит их слияние.
Для ветвления проекта зайдите на его страницу и щелк­ните
на кнопке Fork в ее верхнем правом углу (рис. 6.8).
Через несколько секунд вы попадете на новую страницу
проекта с доступной для редактирования копией кода.
Рис. 6.8. Кнопка Fork

Схема работы с GitHub
Сайт GitHub спроектирован под определенную схему работы, сконцентрированную
вокруг запросов на включение. Она применяется как сплоченными командами,
пользующимися общим репозиторием, так и распределенными группами и даже
группами не знакомых друг с другом людей, добавляющих свою работу к проекту
на десятках ветвлений. Эта схема организована по принципу тематических веток,
с которыми вы познакомились в главе 3.
Вот как это работает в общем случае:
1. На основе ветки master создается тематическая ветка.
2. Делается ряд коммитов с целью внести улучшения в проект.
3. Ветвь отсылается в ваш GitHub-проект.
4. На сайте GitHub открывается запрос на включение.

176    Глава 6 • GitHub
5. Предложенное изменение обсуждается и, возможно, подвергается редактированию.
6. Владелец проекта выполняет слияние или закрывает запрос на включение.
По сути, это рассмотренная в главе 5 схема работы при наличии диспетчера интеграции, но обсуждение и анализ кода осуществляются не по электронной почте,
а посредством GitHub-инструментария.
Рассмотрим работу этой схемы на примере.

Запрос на включение
В поисках кода для программируемого микроконтроллера Arduino Тони обнаруживает на странице https://github.com/schacon/blink замечательную программу (рис. 6.9).

Рис. 6.9. Проект, в который мы хотим внести свой вклад

Единственной проблемой является слишком высокая скорость мигания. С нашей
точки зрения, между изменениями состояния лучше ждать три секунды, а не одну.
Поэтому отредактируем программу и отправим ее в проект как предлагаемое изменение.

Содействие проекту   177

Первым делом мы щелкаем на кнопке Fork, как описывалось в предыдущем разделе,
для получения себе копии проекта. Имя пользователя в данном случае tonychacon,
поэтому копия проекта оказывается по адресу https://github.com/tonychacon/blink, где
мы и сможем заняться ее редактированием. Мы локально ее клонируем, создадим
тематическую ветку, внесем в код изменения и, наконец, отправим исправленный
код обратно на сайт GitHub.
$ git clone https://github.com/tonychacon/blink (1)
Cloning into 'blink'...
$ cd blink
$ git checkout -b slow-blink (2)
Switched to a new branch 'slow-blink'
$ sed -i '' 's/1000/3000/' blink.ino (3)
$ git diff --word-diff (4)
diff --git a/blink.ino b/blink.ino
index 15b9911..a6cc5a5 100644
--- a/blink.ino
+++ b/blink.ino
@@ -18,7 +18,7 @@ void setup() {
// the loop routine runs over and over again forever:
void loop() {
digitalWrite(led, HIGH); // turn the LED on (HIGH is the voltage level)
[-delay(1000);-]{+delay(3000);+} // wait for a second
digitalWrite(led, LOW); // turn the LED off by making the voltage LOW
[-delay(1000);-]{+delay(3000);+} // wait for a second
}
$ git commit -a -m 'three seconds is better' (5)
[master 5ca509d] three seconds is better
1 file changed, 2 insertions(+), 2 deletions(-)
$ git push origin slow-blink (6)
Username for 'https://github.com': tonychacon
Password for 'https://tonychacon@github.com':
Counting objects: 5, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 340 bytes | 0 bytes/s, done.
Total 3 (delta 1), reused 0 (delta 0)
To https://github.com/tonychacon/blink
* [new branch] slow-blink -> slow-blink

Вся процедура выглядит так:
1. Локальное клонирование нашей ветки проекта.
2. Создание нагляднойтематической ветки.
3. Внесение изменений в код.
4. Проверка корректности изменений.

178    Глава 6 • GitHub
5. Фиксация наших изменений в тематической ветке.
6. Отправка новой тематической ветки в нашу ветку на сайте GitHub.
Вернувшись к нашей ветке на сайте GitHub, мы увидим, что система получила
нашу тематическую ветку и предоставила большую зеленую кнопку для перехода к нашим изменениям и открытия запроса на включение в исходный проект
(рис. 6.10).

Рис. 6.10. Кнопка Pull Request

Кроме того, вы можете перейти на страницу с перечнем веток по адресу https://
github.com///branches, найти там нужную ветку
и открыть запрос на включение оттуда.
Щелчок на этой зеленой кнопке откроет экран, где изменению, для которого мы
собираемся создать запрос на включение, можно присвоить заголовок и снабдить
его описанием, чтобы у владельца проекта появился стимул с ним ознакомиться.
Как правило, имеет смысл приложить усилия и сделать описание как можно более
содержательным, чтобы сразу можно было понять, зачем предлагается данное изменение и почему его желательно принять (рис. 6.11).
Кроме того, мы увидим список коммитов в нашей тематической ветке, которые
«опережают» ветку master (в данном случае есть всего один такой коммит) и
­ унифицированное описание всех изменений, к которым приведет слияние этой ветки
с проектом.

Содействие проекту   179

После щелчка на кнопке Create Pull Request владелец проекта, для которого вы создали ответвление, получит уведомление о предлагаемом изменении и ссылку на
страницу с полной информацией о нем.

Рис. 6.11. Страница создания запроса на включение

ПРИМЕЧАНИЕ
Как правило, запросы на включение используются в открытых проектах, в рамках
которых участники предлагают полностью готовые к внедрению решения, но прибегают к ним и во внутренних проектах в начале цикла разработки. Так как добавлять
информацию в тематическую ветку можно и после открытия запроса на включение,
запрос часто открывается на ранней стадии и используется как способ выполнения
циклов работы одной командой.

180    Глава 6 • GitHub

Стадии обработки запроса на включение
Теперь владелец проекта может ознакомиться с предложенным изменением и выполнить его слияние, отвергнуть его или оставить к нему комментарий. Предположим, общая идея ему понравилась, но ему бы хотелось, чтобы в выключенном
состоянии лампочка находилась дольше, чем во включенном.
В рабочей схеме, рассмотренной в главе 5, обсуждение этого аспекта велось бы
по электронной почте, но GitHub позволяет делать это непосредственно на сайте.
Владелец проекта может изучить вносимые поправки, выделить строку кода и написать для нее комментарий (рис. 6.12).

Рис. 6.12. Комментарий к строке кода в запросе на включение

Как только владелец добавит этот комментарий, пользователь, открывший запрос
на включение (как и все остальные пользователи, работающие с данным репозиторием), получит уведомление. О том, как настроить уведомления, мы поговорим чуть
позже, но если у Тони соответствующий режим включен, он получит по электронной
почте сообщение, представленное на рис. 6.13.
Все пользователи могут оставлять комментарии к запросу на включение. На рис. 6.14
показана ситуация, когда владелец проекта сначала комментирует строку кода,
а затем оставляет общий комментарий в разделе обсуждений. Обратите внимание,
что в обсуждение включены также комментарии к коду.
Теперь предложивший изменение участник видит, что следует сделать, чтобы это
изменение было принято. К счастью, это тоже очень просто. Если в случае обмена

Содействие проекту   181

данными по электронной почте вам пришлось бы заново создавать архив и повторно
отправлять его в рассылку, GitHub позволяет выполнить коммит в тематической
ветке и отправить его.

Рис. 6.13. Комментарий, отправленный как уведомление по электронной почте

Рис. 6.14. Страница обсуждения запроса на включение

182    Глава 6 • GitHub
После этого владелец проекта снова получит уведомление, а перейдя на страницу,
увидит, что проблема решена. Более того, так как строка кода, которой был посвящен комментарий, отредактирована, GitHub сворачивает утратившую актуальность
ветку (рис. 6.15).

Рис. 6.15. Финал запроса на включение

Имейте в виду, что на вкладке Files Changed этого запроса на включение вы найдете
описание изменений в унифицированном формате, то есть совокупные изменения,
которые произойдут с главной веткой после того, как туда будет слито содержимое
тематической ветки. Фактически вам покажут результат действия команды git
diff master... для ветки, на основе которой был сформирован данный
запрос на включение. Подробно эта команда рассматривалась в разделе «Просмотр
вносимых изменений» главы 5.
Кроме того, обращаем ваше внимание, что GitHub проверяет, корректно ли происходит слияние изменений, предлагаемых в запросе, и предоставляет кнопку для
выполнения этой операции на сервере. Эта кнопка появляется только при наличии
у вас доступа на запись в репозиторий и возможности аккуратного слияния. После

Содействие проекту   183

щелчка на ней GitHub выполняет слияние без перемотки. Даже если перемотка
возможна, будет создан только коммит слияния.
При желании вы можете извлечь данные из ветки и выполнить слияние локально.
Когда вы сольете эту ветку в свою ветку master и отправите ее на сервер GitHub,
запрос на включение автоматически закроется.
Так выглядит базовая рабочая схема большинства проектов на сайте GitHub. Создаются тематические ветки, для них открываются запросы на включение, затем следует
обсуждение, возможно, в ветке выполняется дополнительная работа, и в конечном
счете запрос либо закрывается, либо подвергается слиянию.
НЕ ТОЛЬКО ВЕТВЛЕНИЯ
Имейте в виду, что запрос на включение может быть открыт для двух веток одного
репозитория. Если вы работаете над каким-то программным компонентом вместе с
коллегой и у вас обоих есть доступ на запись, можно отправить тематическую ветку
в репозиторий и открыть запрос на ее включение в ветку master этого же проекта,
чтобы инициировать анализ кода и процесс обсуждения. Как видите, ветвления не
требуется.

Более сложные запросы на включение
Теперь, когда вы знаете базовую схему участия в проектах на сайте GitHub, рассмотрим интересные аспекты применения запросов на включение, позволяющие
повысить эффективность вашей работы.

Запросы на включение как исправления
Важно понимать, что в большинстве случаев никто не воспринимает запросы на
включение как очередь ценных исправлений, которые следует применять строго в
определенном порядке, как это бывает в проектах, где наборы исправлений присылают по электронной почте. Для большинства GitHub-проектов ветки с запросами
на включение — это повод многократно обсудить предлагаемые изменения, получив
в конце унифицированный код, который применяется путем слияния.
Это важное отличие, ведь, как правило, изменение предлагается до того, как код
можно будет считать идеальным, — для вкладов, осуществляемых через списки рассылки, это практически невероятная ситуация. Однако такой подход способствует
возникновению обсуждения на ранних стадиях работы, и появившееся в итоге
корректное решение представляет собой в большей степени плод коллективных
усилий. Когда появляется код с запросом на включение, а владелец предлагает
внести в него изменения, последовательность исправлений не перематывается, вместо этого добавление отправляется в ветку как новый коммит, двигая обсуждение
вперед, но в контексте предыдущей работы.

184    Глава 6 • GitHub
К примеру, вернемся к рис. 6.15. Обратите внимание, что участник не перемещает
свой коммит, посылая еще один запрос на включение. Он добавляет новые коммиты и отправляет их в существующую ветку. При таком подходе, вернувшись в
будущем к данному запросу на включение, вы легко сможете понять, почему было
принято то или иное решение. Щелчок на кнопке Merge приводит к появлению
коммита слияния со ссылкой на запрос, что дает возможность при необходимости
легко вернуться к исходному обсуждению.

Сохранение актуальности данных
Если ваш запрос на включение устаревает или по каким-то другим причинам не
допускает чистого слияния, ситуацию следует исправить, чтобы владелец проекта
смог без проблем добавить предложенные вами изменения. GitHub проверяет этот
аспект и в нижней части каждого запроса на включения сообщает, можно ли без
проблем осуществить слияние.
Если вы видите такую запись, как на рис. 6.16, значит, в ветку следует внести
­исправления, чтобы владельцу проекта не пришлось делать дополнительную
ра­боту. Как только конфликт слияния будет разрешен, цвет рамки изменится
­на зеленый.

Рис. 6.16. Запрос на включение, не допускающий аккуратного слияния

Это можно сделать двумя способами. Во-первых, переместив свою ветку на вершину
целевой ветки (как правило, это ветка master репозитория, послужившего основой
вашего ветвления), во-вторых, слив содержимое целевой ветки в вашу ветку.
Большинство использующих GitHub разработчиков выбирает второй вариант
по причинам, изложенным в предыдущем разделе. Значение имеют только история
и финальное слияние, а перемещение всего лишь делает вашу историю немного
чище, являясь при этом операцией намного более сложной и способствующей
ошибкам.
Если вы хотите выполнить слияние в целевую ветку, чтобы сделать ваш запрос на
включение допускающим слияние, добавьте себе исходный репозиторий в качестве
удаленного, извлеките оттуда данные, слейте содержимое основной ветки этого
репозитория в вашу тематическую ветку, разрешите конфликты и, наконец, верните
все в ветку, для которой открывался запрос на включение.
Предположим, что в рассмотренный ранее пример «tonychacon» автор внес изменение, которое стало причиной конфликта в запросе на включение. Рассмотрим
наши дальнейшие действия.

Содействие проекту   185
$ git remote add upstream https://github.com/schacon/blink (1)
$ git fetch upstream (2)
remote: Counting objects: 3, done.
remote: Compressing objects: 100% (3/3), done.
Unpacking objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 0 (delta 0)
From https://github.com/schacon/blink
* [new branch] master -> upstream/master
$ git merge upstream/master (3)
Auto-merging blink.ino
CONFLICT (content): Merge conflict in blink.ino
Automatic merge failed; fix conflicts and then commit the result.
$ vim blink.ino (4)
$ git add blink.ino
$ git commit
[slow-blink 3c8d735] Merge remote-tracking branch 'upstream/master' \
into slower-blink
$ git push origin slow-blink (5)
Counting objects: 6, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 682 bytes | 0 bytes/s, done.
Total 6 (delta 2), reused 0 (delta 0)
To https://github.com/tonychacon/blink
ef4725c..3c8d735 slower-blink -> slow-blink

Вся процедура выглядит так:
1. Добавление исходного хранилища как удаленного с именем upstream.
2. Извлечение последних наработок из этого удаленного репозитория.
3. Слияние основной ветки в вашу тематическую ветку.
4. Решение возникающего конфликта.
5. Возвращение данных в ту же самую тематическую ветку.
После этого запрос на включение автоматически обновляется и производится проверка допустимости его слияния (рис. 6.17).

Рис. 6.17. Теперь слияние запроса на включение можно выполнить без проблем

186    Глава 6 • GitHub
Одним из замечательных аспектов Git является возможность выполнять эти
­действия в непрерывном режиме. В случае долгоживущего проекта слияние из
целевой ветки можно выполнять снова и снова, просто разбираясь с конфликтами,
появившимися после последнего слияния, что делает процесс полностью управляемым.
Если же вам очень хочется прибегнуть к перемещению, ни в коем случае не отправляйте данные в ветку, для которой уже открыт запрос на включение. Если другие
пользователи извлекут оттуда информацию и начнут с ней работу, вы столкнетесь
с проблемами, описанными в разделе «Риски, связанные с перемещением» главы 3.
Вместо этого отправьте содержимое перемещенной ветки в новую ветку на сайте
GitHub, откройте совершенно новый запрос на включение, ссылающийся на старый,
а затем закройте оригинал.

Ссылки
У вас может возникнуть вопрос «Как сделать ссылку на старый запрос на включение?» Сослаться на другой элемент, расположенный практически в любом доступном для записи месте сайта GitHub, можно множеством способов.
Для начала рассмотрим перекрестные ссылки на другой запрос на включение или
проблему. Всем запросам на включение и проблемам присваиваются уникальные
в пределах проекта номера. К примеру, у вас не может быть одновременно запроса на включение под номером 3 и проблемы под номером 3. Для ссылки на чужой
запрос или проблему в рамках одного проекта достаточно указать # в
любом комментарии или описании. Если вы ссылаетесь на проблему или запрос
в ветке репозитория, в котором вы в данный момент находитесь, укажите имя
пользователя#. Запись вида имя пользователя/репозиторий#
является ссылкой на запрос или проблему из чужого репозитория.
Рассмотрим пример. Предположим, мы переместили ветку из предыдущего примера, создали для нее новый запрос на включение и хотим добавить в него ссылку
на старый вариант запроса. Еще нам требуется ссылка на проблему в ветке текущего
репозитория и на еще одну проблему из чужого проекта. Заполним описание, как
показано на рис. 6.18.
После отправки запроса на включение наши ссылки приобретут вид, показанный
на рис. 6.19.
Обратите внимание, что полный URL-адрес, вставленный в комментарий, GitHub
сокращает, оставляя только необходимую информацию.
Теперь если Тони закроет исходный запрос на включение, из-за того, что он упоминается в новом запросе, GitHub автоматически создаст событие уведомления
(trackback) на временной шкале этого запроса. В итоге любой, кто зайдет на страницу этого запроса и увидит, что он закрыт, сможете легко перейти к заместившей
его более новой версии. Пример такой ссылки показан на рис. 6.20.

Содействие проекту   187

Рис. 6.18. Перекрестные ссылки в запросе на включение

Рис. 6.19. Перекрестные ссылки в запросе на включение

Рис. 6.20. Перекрестные ссылки в запросе на включение

188    Глава 6 • GitHub
Ссылаться можно не только на проблему по ее номеру, но и на коммит по его контрольной сумме SHA. Указывать нужно все 40 символов в этом случае GitHub,
обнаружив контрольную сумму в комментарии, создает прямую ссылку на коммит.
Ссылки на коммиты в ветвлениях и других репозиториях делаются таким же способом, как и ссылки на проблемы.

Язык разметки Markdown
Создание ссылок на проблемы — всего лишь первый шаг ко многим интересным
вещам, которые можно проделывать практически с любым текстовым полем на
сайте GitHub. В описаниях проблем и запросов на включение, комментариях, комментариях кода и пр. можно использовать так называемую GitHub-версию языка
Markdown. Вы пишете обычный текст, который визуализируется в отформатированном виде, как показано на рис. 6.21.

Рис. 6.21. Пример написания текста на языке Markdown с его последующей
визуализацией

GitHub-версия языка Markdown
В GitHub-версии к базовому синтаксису языка Markdown добавляются новые
элементы. Они пригодятся вам при подготовке как исчерпывающих комментариев
к запросам на включение и проблемам, так и описаний.

Списки задач
Первой характерной для GitHub особенностью языка Markdown, особенно часто
фигурирующей в запросах на включение, является список задач. Он представляет

Содействие проекту   189

собой набор флажков с перечнем вещей, которые требуется сделать. Вставка такого
списка в проблему или запрос на включение обычно означает появление набора
задач, без решения которых работу нельзя считать завершенной.
Вот пример списка, указывающего, что вам нужно написать код, все тесты к нему
и снабдить его документацией:
- [X] Write the code
- [ ] Write all the tests
- [ ] Document the code

Вывод такого списка в составе запроса на включение или проблемы показано
на рис. 6.22.

Рис. 6.22. Список задач, отображенный в виде комментария
на языке Markdown

Такие списки часто вставляют в запросы на включение, указывая, что именно
следует сделать, прежде чем запрос будет готов к слиянию. Что замечательно, для
обновления комментария достаточно установить или сбросить флажок — редактировать сам Markdown-код не требуется.
Более того, GitHub ищет списки задач в ваших проблемах и запросах на включение
и выводит их на страницах, где они фигурируют, в виде метаданных. К примеру,
при просмотре страниц с перечнем всех запросов на включение можно увидеть
результативность работы над каждым запросом (рис. 6.23). Подобное деление запросов на наборы задач помогает другим пользователям отслеживать ход работы
в ветке.

Рис. 6.23. Отчет о решении задач из списка в перечне запросов
на включение

190    Глава 6 • GitHub
Списки задач особенно полезны, когда вы открываете запрос на включение на
ранней стадии и следите за тем, как продвигается реализация предложенного программного компонента.

Фрагменты кода
Еще к комментариям можно добавлять фрагменты кода. Это имеет смысл, например, чтобы показать, что вы собираетесь делать, перед попыткой реализовать задуманное в виде коммита в своей ветке. Также этой возможностью пользуются для
демонстрации кода, над которым работа еще не ведется, но который может быть
реализован в рамках текущего запроса на включение.
Для добавления фрагмента кода достаточно заключить его в обратные кавычки.
```java
for(int i=0 ; i < 5 ; i++)
{
System.out.println("i is : " + i);
}
```

Если, как в приведенном примере, указать язык, на котором написан фрагмент,
GitHub попытается выполнить подсветку синтаксиса. Результат визуализации
этого фрагмента показан на рис. 6.24.

Рис. 6.24. Визуализация фрагмента кода, заключенного в кавычки

Цитирование
При ответе на небольшой фрагмент длинного комментария его можно выборочно
вставить в свой текст, поставив в начале строки символ >. Этот прием применяется
настолько часто, что для него существует даже клавиатурная комбинация. Нужно
выделить текст, на который вы собираетесь отвечать, и нажать клавишу r. Выделенный текст немедленно будет оформлен в виде цитаты.
Цитата выглядит вот так:
> Whether 'tis Nobler in the mind to suffer
> The Slings and Arrows of outrageous Fortune,
How big are these slings and in particular, these arrows?

На рис. 6.25 демонстрируется ее вид после визуализации.

Содействие проекту   191

Рис. 6.25. Пример визуализации цитаты

Эмодзи
Наконец, в комментариях можно пользоваться эмодзи — языком идеограмм и
смайликов. Элементы этого языка довольно широко применяются в комментариях,
и вы часто будете встречать их на сайте GitHub. Существует даже руководство по
этому языку. Например, если при наборе комментария ввести символ двоеточия,
функция автозаполнения поможет найти подходящий вариант (рис. 6.26).

Рис. 6.26. Автозаполнение для языка эмодзи в действии

В комментариях выражения языка эмодзи имеют форму ::. К примеру, вы
можете написать:
I :eyes: that :bug: and I :cold_sweat:.
:trophy: for :microscope: it.
:+1: and :sparkles: on this :ship:, it's :fire::poop:!
:clap::tada::panda_face:

После визуализации вы получите комментарий, показанный на рис. 6.27.

192    Глава 6 • GitHub

Рис. 6.27. Комментарий, перенасыщенный символами эмодзи

Особой смысловой нагрузки это не несет, просто добавляет немного эмоций в среду,
в которой другим способом трудно выразить свои чувства.
ПРИМЕЧАНИЕ
Существует немало веб-служб, активно пользующихся символами языка эмодзи. Большая
шпаргалка для выбора наиболее подходящего для выражения ваших чувств символа
находится на странице http://www.emoji-cheat-sheet.com/.

Изображения
С технической точки зрения эта крайне полезная функция уже не относится
к GitHub-версии языка Markdown. В дополнение к возможности добавлять в комментарии ссылки на изображения, для случаев, когда URL-адрес изображения
бывает сложно найти и внедрить, GitHub позволяет осуществлять внедрение простым перетаскиванием картинок в текстовое поле (рис. 6.28).

Рис. 6.28. Перетаскивание изображения в текстовое поле для его закачки
и последующего автоматического внедрения

Сопровождение проекта   193

Если вернуться к рис. 6.18, вы увидите справа над полем ввода текста небольшой
значок Parsed as Markdown. Щелчок на нем вызывает шпаргалку с информацией обо
всех операциях на языке Markdown в GitHub.

Сопровождение проекта
Теперь, когда вы знаете, каким образом вносить вклад в чужие проекты, посмотрим
на ситуацию с другой стороны и поговорим о создании, сопровождении и администрировании собственных проектов.

Создание нового репозитория
Создадим новый репозиторий, чтобы получить возможность делиться кодом своего
проекта с другими людьми. Первым делом щелкните на кнопке New repository, расположенной справа на панели инструментов (рис. 6.29), или на кнопке + панели
инструментов рядом с вашим именем пользователя (рис. 6.30).

Рис. 6.29. Область Your repositories

Рис. 6.30. Раскрывающийся список
New repository

Появится форма создания нового репозитория (рис. 6.31).
Здесь в обязательном порядке следует указать только название проекта; все
остальные поля заполняются по желанию. Достаточно щелчка на кнопке Create
Repository, и у вас появится новый репозиторий на сайте GitHub с именем вида /.
Так как кода у вас пока нет, GitHub показывает инструкцию, касающуюся того, как
создать совершенно новый Git-репозиторий или подключиться к существующему
проекту Git. Мы не будем еще раз объяснять этот материал; если вам требуется
освежить его в памяти, перечитайте главу 2.
URL-адрес размещенного на сайте GitHub проекта можно дать всем, с кем вы
хотите поделиться результатами своего труда. Доступ к любому GitHub-проекту

194    Глава 6 • GitHub
осуществляется по протоколу HTTP в форме https://github.com/< пользова­
тель>/ или по протоколу SSH в форме git@github.com:< поль­
зователь>/. Система Git может извлекать данные по любому
из этих URL-адресов, но уровень доступа зависит от учетных данных человека,
который осуществляет подключение.

Рис. 6.31. Форма создания нового репозитория
ПРИМЕЧАНИЕ
В случае открытого проекта зачастую лучше публиковать URL-адрес на базе HTTP, чтобы
для его клонирования не требовалась учетная запись на сайте GitHub. Если вы дадите
пользователю адрес доступа по протоколу SSH, ему потребуется создать учетную ­запись
и загрузить свой ключ SSH. В случае же доступа по протоколу HTTP достаточно ввести
URL-адрес в адресную строку браузера.

Добавление соавторов
Если вы работаете с другими пользователями, которым требуется право на запись коммитов, их следует добавить как соавторов. Если у Бена, Джеффа и Луизы есть учетные записи на сайте GitHub и вы хотите предоставить им доступ
на з­ апись в ваш репозиторий, добавьте их к своему проекту. После этого они
смогут как читать материалы вашего проекта, так и вносить свой вклад в ваш Gitрепозиторий.
Щелкните на кнопке Settings внизу расположенной справа панели (рис. 6.32).

Сопровождение проекта   195

Рис. 6.32. Ссылка на страницу настройки репозитория

Выберите в появившемся слева меню пункт Collaborators. После этого остается ввести в текстовое поле имя пользователя и щелкнуть на кнопке Add collaborator. Этот
процесс можно повторять произвольное количество раз, предоставляя доступ всем,
кому считаете нужным. Чтобы лишить кого-то права доступа, щелкните на крестике,
расположенном справа от имени этого пользователя (рис. 6.33).

Рис. 6.33. Соавторы репозитория

Управление запросами на включение
Теперь, когда у вас есть проект с каким-то кодом, а возможно, и несколько соавторов, обладающих доступом на запись, рассмотрим процесс обработки запросов
на включение.
Запросы на включение могут появиться как из ветки в ветвлении репозитория, так
и из другой ветки вашего собственного репозитория. Единственное отличие состоит в том, что в первом случае это запросы от людей, не имеющих права на запись

196    Глава 6 • GitHub
в указанную ветку и в ваши ветки, а во втором их присылают люди, как правило,
имеющие возможность делать запись в ветку.
Предположим, что вы — пользователь «tonychacon», который только что создал
новый проект с кодом для системы Arduino и присвоил ему имя «fade».

Уведомления по электронной почте
Пусть какой-то пользователь внес изменения в ваш код и прислал вам запрос на
включение. Вы должны получить сообщение электронной почты, уведомляющее
вас о новом запросе. Примерно такое, как на рис. 6.34.

Рис. 6.34. Электронное письмо с уведомлением о новом запросе на включение

Следует отметить несколько моментов. В этом письме вы увидите сведения
о внесенных изменениях — перечень отредактированных пользователем файлов
с указанием степени их редактирования. Здесь же содержится ссылка с адресом
запроса на включение на сайте GitHub. И несколько URL-адресов, которые можно
использовать из командной строки.
Возможно, вы обратили внимание на строку git pull patch-1. Это простой
механизм извлечения данных из удаленной ветки без добавления удаленного репозитория. Этот процесс кратко рассматривался в разделе «Проверка удаленных

Сопровождение проекта   197

веток» главы 5. При желании вы можете создать тематическую ветку, перейти
в нее и воспользоваться этой командой для добавления предложенных в запросе
на включение изменений.
Другие интересные URL-адреса, .diff и .patch, как можно догадаться, ведут
к унифицированным версиям различий и исправлений, связанных с запросом
­на включение. В принципе, наработки из запроса можно добавить вот такой командой:
$ curl http://github.com/tonychacon/fade/pull/1.patch | git am

Совместная работа над запросом на включение
Как следует из раздела «Схема работы с GitHub», теперь у вас поддерживается
диалог с пользователем, открывшим запрос на включение. Вы можете прокомментировать отдельные строки кода, коммит в целом или весь запрос на включение,
пользуясь GitHub-версией языка Markdown.
Каждый раз, когда кто-то оставляет комментарий к запросу на включение, вам
приходит уведомление, чтобы ни одно связанное с запросом действие не осталось
без вашего внимания. Уведомление содержит ссылку на запрос, кроме того, прокомментировать происходящее в цепочке обсуждения запроса можно, просто ответив на письмо (рис. 6.35).

Рис. 6.35. Ответы на электронную почту включаются в цепочку обсуждения

Когда код придет в нужное вам состояние и вы захотите добавить его к себе, его
можно будет скачать и выполнить локальное слияние или воспользоваться уже знакомым вам синтаксисом git pull . Можно даже добавить ветвление
как удаленный репозиторий и применить команды fetch и merge.
Если слияние не представляет проблемы, на сайте GitHub достаточно щелкнуть
на кнопке Merge. При этом произойдет слияние без перемотки, то есть даже если
перемотка возможна, у вас появится всего лишь коммит слияния. Это означает,
что при каждом щелчке на кнопке Merge будет создаваться коммит слияния. Как
показано на рис. 6.36, всю эту информацию GitHub предоставляет по щелчку на
ссылке, ведущей к справочной информации.

198    Глава 6 • GitHub
При желании вы можете выполнить слияние. Или просто закройте запрос — тогда
человеку, который его открыл, придет соответствующее уведомление.

Рис. 6.36. Кнопка Merge и инструкции о том, как вручную выполнить слияние
запроса на включение

Обращения к запросам на включение
Если вам приходится иметь дело с большим количеством запросов на включение
и при этом вы не хотите добавлять множество удаленных репозиториев или каждый раз скачивать информацию, GitHub предлагает специализированный прием,
который может быть вам крайне полезен, но детали реализации которого мы рассмотрим чуть позже.
Для репозитория GitHub визуализирует ветки запросов на включение как некие
псевдоветки на сервере. По умолчанию их содержимое при клонировании не переносится, но присутствует в неявном виде и легко доступно.
Мы продемонстрируем это на примере низкоуровневой команды ls-remote (такие
команды часто называют «служебными», подробно они рассматриваются в главе 10).
При повседневной работе с Git эта команда не используется, но полезна, когда нам
требуется посмотреть список всех ссылок на сервере.
Посмотрим, какой результат она даст в случае с репозиторием blink, с которым
мы работали раньше. Мы получим список всех веток и тегов, а также других имеющихся в репозитории ссылок.
$ git ls-remote https://github.com/schacon/blink
10d539600d86723087810ec636870a504f4fee4d
HEAD
10d539600d86723087810ec636870a504f4fee4d
refs/heads/master
6a83107c62950be9453aac297bb0193fd743cd6e
refs/pull/1/head
afe83c2d1a70674c9505cc1d8b7d380d5e076ed3
refs/pull/1/merge

Сопровождение проекта   199
3c8d735ee16296c242be7a9742ebfbc2665adec1
15c9f4f80973a2758462ab2066b6ad9fe8dcf03d
a5a7751a33b7e86c5e9bb07b26001bb17d775d1a
31a45fc257e8433c8d8804e3e848cf61c9d3166c

refs/pull/2/head
refs/pull/2/merge
refs/pull/4/head
refs/pull/4/merge

Именно такой результат вы получите, запустив из своего репозитория команду
git ls-remote origin или указав другой удаленный репозиторий, который хотите
проверить.
Если репозиторий находится на сайте GitHub и у вас есть открытые запросы на
включение, появится набор ссылок с приставкой refs/pull/. По своей сути это
ветки, но так как они не перечислены в разделе refs/heads/, при клонировании
или извлечении данных с сервера вы их содержимого не получите — в обычном
состоянии команда fetch их игнорирует.
Для каждого запроса на включение есть две ссылки — та, которая заканчивается
символами /head, точно указывает на последний коммит в ветке запроса. Поэтому
если пользователь откроет запрос на включение в наше хранилище, а его ветка
будет называться bug-fix и указывать на коммит a5a775, в нашем репозитории
ветка bug-fix не появится (так как она принадлежит ответвившейся версии), но
мы получим ссылку pull//head на коммит a5a775. Как видите, мы можем
легко извлечь содержимое любой ветки запроса на включение, не добавляя к себе
ветки из удаленных репозиториев.
Попробуем воспользоваться прямой ссылкой для извлечения данных:
$ git fetch origin refs/pull/958/head
From https://github.com/libgit2/libgit2
* branch refs/pull/958/head -> FETCH_HEAD

Эта команда скажет Git: «Подключись к удаленной ветке origin и скачай данные
по ссылке refs/pull/958/head». Система Git скачает все, что вам нужно для конструирования этой ссылки, и поместит указатель на нужный вам коммит в папку
.git/FETCH_HEAD. После этого можно будет воспользоваться командой git merge
FETCH_HEAD и слить данные в ветку, в которой вы собираетесь их тестировать, но
такое сообщение фиксации выглядит несколько странно. Кроме того, вряд ли вам
захочется многократно проделывать эту рутинную операцию, когда придется работать с большим количеством запросов на включение.
К счастью, есть способ извлечь данные из всех запросов на включение и поддерживать их в актуальном состоянии простым подключением к удаленному
репозиторию.
Откройте в своем любимом редакторе файл .git/config и найдите удаленный
репозиторий origin. Это может выглядеть вот так:
[remote "origin"]
url = https://github.com/libgit2/libgit2
fetch = +refs/heads/*:refs/remotes/origin/*

200    Глава 6 • GitHub
Строка, которая начинается с fetch =, обеспечивает проецирование имен в удаленном репозитории на имена в вашей локальной папке .git. По сути, она сообщает
системе Git: «Все, что в удаленном репозитории находится по адресу refs/heads,
должно попасть в папку моего репозитория refs/remotes/origin». Этот раздел
можно отредактировать, добавив еще одну спецификацию ссылок:
[remote "origin"]
url = https://github.com/libgit2/libgit2.git
fetch = +refs/heads/*:refs/remotes/origin/*
fetch = +refs/pull/*/head:refs/remotes/origin/pr/*

Эта последняя строка скажет Git: «Все ссылки вида refs/pull/123/head должны
сохраняться локально по адресу refs/remotes/origin/pr/123». Сохраните этот
файл и выполните команду git fetch:
$ git fetch
# ...
* [new ref] refs/pull/1/head -> origin/pr/1
* [new ref] refs/pull/2/head -> origin/pr/2
* [new ref] refs/pull/4/head -> origin/pr/4
# ...

Теперь все запросы на извлечение информации из удаленного репозитория представлены в виде локальных ссылок, которые функционируют как ветки слежения;
они предназначены только для чтения и обновляются при выполнении команды
fetch. В итоге вы можете без проблем локально протестировать код из запроса на
включение:
$ git checkout pr/2
Checking out files: 100% (3769/3769), done.
Branch pr/2 set up to track remote branch pr/2 from origin.
Switched to a new branch 'pr/2'

Проницательные читатели могли обратить внимание на слово head в конце спе­
цификации удаленных ссылок. На стороне GitHub имеется также ссылка refs/
pull/#/merge, ведущая к коммиту, который мог бы возникнуть, если бы вы щелк­
нули на кнопке Merge. Это позволяет посмотреть на результат слияния до его
­выполнения.

Запросы на включение запросов на включение
Можно открыть запрос на включение не только в ветку main или master, но и в любую другую ветку в сети. Более того, можно открыть запрос на включение в другой
запрос.
Если вы обнаружили перспективный запрос на включение и у вас появилась базирующаяся на нем идея, но вы не уверены в ее правильности или просто у вас
нет доступа на запись в целевую ветку, можно открыть запрос на включение своей
информации в этот запрос.

Сопровождение проекта   201

В верхней части страницы открытия запроса находятся поля, в одном из которых указана ветка, предназначенная для внесения изменений, в другом — ветка,
содержащая предназначенные для включения данные. Щелчок на расположенной
справа от этих полей кнопке Edit дает возможность менять не только ветку, но
и ветвление (рис. 6.37).

Рис. 6.37. Вручную меняем целевое ветвление и ветку запроса на включение

Здесь вы можете легко указать, что содержимое вашей ветки следует сливать в другой­
запрос на включение или в другое ветвление проекта.

Упоминания и уведомления
В GitHub встроена прекрасная система уведомлений, которая приходит на помощь,
когда у вас появляются вопросы или возникает необходимость в обратной связи
с конкретным человеком или группой.
Введенный в любой комментарий символ @ активизирует функцию автозаполнения,
открывающую список всех соавторов или участников проекта (рис. 6.38).
Вы также можете упомянуть пользователя, имя которого отсутствует в раскрываю­
щемся списке, но зачастую функция автозаполнения просто ускоряет данный процесс.
После публикации комментария, в котором упоминается пользователь, этот пользователь получает уведомление. Это весьма эффективный способ привлечения
людей к обсуждению. На сайте GitHub авторы запросов на включение часто привлекают других пользователей из своей рабочей группы или фирмы для анализа
проблемы или запроса.

202    Глава 6 • GitHub

Рис. 6.38. Наберите символ @, чтобы упомянуть конкретного пользователя

Человек, упомянутый в проблеме или запросе на включение, подписывается на него
и начинает получать уведомления обо всех совершаемых в данной теме действиях.
Подписка появляется и при открытии какого-то элемента, просмотре репозитория
или комментировании в чужой теме. Если вы не хотите получать уведомления,
воспользуйтесь кнопкой Unsubscribe (рис. 6.39).

Рис. 6.39. Кнопка, позволяющая отписаться от обновлений, связанных с проблемой
или запросом на включение

Страница уведомлений
Когда мы говорим об «уведомлениях» в контексте GitHub, мы подразумеваем некий способ, которым сайт пытается сообщить о возникновении некоего события.
Вы можете выбрать удобный для себя вариант. Это делается на вкладке Notification
center страницы настройки (рис. 6.40).
Вы можете получать уведомления по электронной почте или просматривать их
в Интернете. Существует возможность выбрать один из этих вариантов, ни одного или оба сразу, причем как для случая, когда вы принимаете активное участие
в каких-то событиях, так и для деятельности, связанной с просматриваемыми вами
репозиториями.
Уведомления через Интернет. Уведомления через Интернет находятся на сайте
GitHub, а значит, увидеть их можно только на этом сайте. Если вы выбрали этот
вариант получения уведомлений, об их появлении сообщит маленькая синяя точка
над значком уведомлений в верхней части экрана, показанная на рис. 6.41.

Сопровождение проекта   203

Рис. 6.40. Вкладка Notification center

Рис. 6.41. Центр уведомлений

Щелчок на этом значке откроет сгруппированный по проектам список всех элементов, о которых вам пришли уведомления. Чтобы отобразить только уведомления,
связанные с конкретным проектом, выделите имя этого проекта на панели слева.
Можно подтвердить принятие уведомления, щелкнув на расположенной справа от
него галочке. Щелчок на галочке рядом с именем группы отметит все связанные с
этой группой уведомления как прочитанные. Рядом с каждой галочкой находится
также значок в виде рупора, щелчок на котором отключает дальнейшие уведомления, связанные с этим элементом.
Все эти инструменты крайне полезны при работе с большим количеством уведомлений. Многие пользователи сайта GitHub предпочитают полностью отказаться

204    Глава 6 • GitHub
от уведомлений по электронной почте и просматривать их непосредственно на
сайте.
Уведомления по электронной почте. Второй вариант работы с GitHub-уве­
домлениями связан с электронной почтой. Если вы выбрали этот вариант, каждое
уведомление будет присылаться вам по электронной почте. Примеры таких уведомлений вы видели на рис. 6.13 и 6.34. Сообщения корректно разбиты на цепочки,
что очень удобно, если ваш почтовый клиент умеет их визуализировать.
В заголовок сообщения встроено множество метаданных, позволяющих настраивать фильтры и правила. К примеру, в показанном на рис. 6.34 заголовке письма,
присланного Тони, мы найдем следующую информацию:
To: tonychacon/fade
Message-ID:
Subject: [fade] Wait longer to see the dimming effect better (#1)
X-GitHub-Recipient: tonychacon
List-ID: tonychacon/fade
List-Archive: https://github.com/tonychacon/fade
List-Post:
List-Unsubscribe: ,...
X-GitHub-Recipient-Address: tchacon@example.com

Обратим ваше внимание на пару интересных моментов. Если вы хотите выделить
или изменить маршрут почты для этого конкретного проекта или даже запроса на
включение, в поле Message-ID вы найдете все нужные для этого данные в формате
///. К примеру, если бы сообщение касалось
проблемы, в поле фигурировало бы слово «issues», а не «pull». Присутствие полей List-Post и List-Unsubscribe означает, что при наличии почтового
клиента, который умеет с ними работать, вы можете легко отпра­вить сообщение
в обсуждение или отписаться от него. Последнее аналогично щелчку на значке с
изображением рупора на интернет-версии страницы уведомлений или щелчку на
кнопке Unsubscribe на странице запроса на включение или проблемы.
Заметим также, что если у вас включены режимы получения обоего типа уведомлений и в вашем почтовом клиенте разрешен показ изображений, прочитанное
сообщение электронной почты приведет к тому, что в интернет-версии это уведомление тоже будет фигурировать как прочитанное.

Специальные файлы
Существуют файлы, наличие которых в вашем репозитории не пройдет незамеченным для GitHub.

README
Во-первых, это файл README, который может иметь любой формат, распознаваемый
сайтом GitHub как текстовый. Например, он может называться README, README.md,

Сопровождение проекта   205

README.asciidoc и т. п. Обнаружив этот файл среди вашего кода, GitHub отобразит

его на целевой странице проекта.

Многие группы помещают в этот файл всю связанную с проектом информацию,
необходимую пользователям, которые впервые попали в данный репозиторий.
Как правило, там указывается следующая информация:
‰‰ назначение проекта;
‰‰ особенности его конфигурации и установки;
‰‰ пример применения проекта или его запуска;
‰‰ лицензия, по которой доступен этот проект;
‰‰ способ внести в него вклад.

Так как GitHub показывает содержимое этого файла, туда можно встроить изображения или ссылки, облегчающие понимание материала.

CONTRIBUTING
Второй распознаваемый GitHub специальный файл называется CONTRIBUTING.
Обнаружив файл с таким именем и произвольным расширением, GitHub в момент
открытия пользователем запроса на включение визуализирует его содержимое, как
показано на рис. 6.42.

Рис. 6.42. Открытие запроса на включение при наличии файла CONTRIBUTING

Здесь перечисляется то, что вы хотите или не хотите видеть в присылаемых в ваш
проект запросах на включение. Пользователи могут ознакомиться с вашими пожеланиями перед открытием запроса.

206    Глава 6 • GitHub

Администрирование проекта
В общем случае существует не так уж много операций, связанных с администрированием единственного проекта, но с некоторыми возможностями нам бы хотелось
вас познакомить.

Изменение ветки, предлагаемой по умолчанию
Если в качестве ветки, предлагаемой по умолчанию, у вас фигурирует ветка, отличная от ветки master, и вы хотите, чтобы именно для нее пользователями открывались запросы на включение и именно она визуализировалась по умолчанию,
перейдите на вкладку Options страницы настройки вашего хранилища (рис. 6.43).

Рис. 6.43. Изменение ветки, по умолчанию используемой для проекта

Просто выберите в раскрывающемся списке ветку, которая будет фигурировать как
предлагаемая по умолчанию для большинства операций, в том числе как ветка, на
которую выполняется переход при клонировании репозитория.

Пересылка проекта
Для тех, кто хочет переслать проект другому пользователю или организации,
на ­сайте GitHub в нижней части все той же вкладки Options есть кнопка Transfer
(рис. 6.44).

Рис. 6.44. Пересылка проекта другому пользователю сайта GitHub
или организации

Управление организацией   207

Это может пригодиться в ситуации, когда вы больше не хотите заниматься проектом,
а кто-то готов взяться за его поддержку, или когда проект настолько разросся, что
вы предпочитаете передать его какой-нибудь организации.
Эта операция не только перемещает в другое место репозиторий со всеми, кто за ним
наблюдает, но и настраивает перенаправление с вашего URL-адреса на ­новый.
Перенаправлению подвергаются даже Git-команды clone и fetch, а не только
веб-запросы.

Управление организацией
Кроме учетных записей пользователей сайт GitHub допускает так называе­мые
учетные записи организаций. Такие учетные записи имеют свое пространство
имен, в котором существуют все их проекты, но ряд других аспектов работы с­ ними
отличается от работы с обычными учетными записями. Ведь в данном случае речь
идет о группе пользователей, которые одновременно являются владельцами проектов, поэтому существует множество инструментов для управления их подгруппами. Как правило, подобные учетные записи используются проектами с открытым
исходным кодом (такими как Perl или Rails) или компаниями (такими как Google
или Twitter).

Основные сведения об организации
Создать организацию очень просто: щелкните на значке + в верхнем правом углу
любой страницы сайта GitHub и выберите в появившемся меню команду New
organization (рис. 6.45).

Рис. 6.45. Выбор в меню команды New organization

Первым делом следует указать имя вашей организации и адрес электронной
­почты, который будет служить для связи с группой. После этого можно пригласить других пользователей присоединиться к вам в качестве владельцевучетной

208    Глава 6 • GitHub
записи. Как и персональные учетные записи, учетные записи организаций бесплатны, пока речь идет о хранении открытого кода.
Как владелец при ветвлении репозитория вы можете поместить ветку в пространство
имен вашей организации. Создавать новые репозитории вы можете как от имени
своей личной учетной записи, так и от имени любой организации, владельцем которой вы являетесь. Кроме того, вы автоматически получаете доступ на просмотр
любого репозитория, созданного от имени этих организаций.
Как и при работе с персональной учетной записью, вы можете добавить в учетную
запись организации аватар, чтобы выделить ее на общем фоне. Будет у вас и основная
страница с перечнем всех принадлежащих организации репозиториев, доступная
для просмотра всем пользователям.
Теперь поговорим о том, что возможно только для учетной записи организации.

Группы
С отдельными пользователями организации связаны через группы, объединяющие
пользовательские учетные записи и репозитории по уровням доступа.
Предположим, у организации есть три репозитория: f r o n t e n d , b a c k e n d
и deployscripts. Вам хотелось бы, чтобы у разработчиков, использующих HTML/
CSS/Javascript, был доступ к репозиторию frontend и, может быть, к репо­
зиторию backend , а у отдела эксплуатации — доступ к репозиториям backend
и deployscripts. Группы позволяют легко реализовать это на практике, избавляя
вас от необходимости предоставлять коллегам доступ к каждому репозиторию
в отдельности.
Страница Organization содержит простую панель управления всеми принадлежащими
вашей организации репозиториями, пользователями и группами (рис. 6.46).
Чтобы перейти к управлению группами, щелкните справа на боковой панели Teams
(см. рис. 6.46). Откроется страница, на которой вы можете до­бавлять в группу новых
членов, добавлять доступные группе репозитории и управлять настройками уровня
доступа для группы. У каждой группы может быть доступ только на чтение, на
чтение/запись или на администрирование репозиториев. Для изменения уровня
доступа щелкните на кнопке Settings, как показано на рис. 6.47.
Человек, приглашенный в группу, получает уведомление. Также не стоит забывать про упоминания (например, @acmecorp/frontend), которые функционируют
так же, как в случае индивидуальных пользователей, но подписку на обсуждение
по­лучают все члены группы. Это полезно в ситуации, когда требуется привлечь
внимание кого-то из членов группы, но вы не знаете, к кому именно имеет смысл
обратиться.

Управление организацией   209

Рис. 6.46. Страница Organization

Рис. 6.47. Страница Team

Пользователь может состоять в произвольном числе групп, поэтому не огра­ни­
чивайте свое членство группами контроля доступа. Группы по интересам, на­
пример ux, css или refactoring, пригодятся при обсуждении одного типа вопросов, в то время как в группах legal и colorblind обсуждаются проблемы совсем
другого рода.

210    Глава 6 • GitHub

Журнал регистрации
Владелец имеет доступ ко всей информации, связанной с управляемой им организацией. На вкладке Audit Log можно увидеть все события, происшедшие на уровне
организации, а также кем и где они были инициированы (рис. 6.48).
Вы можете выполнять фильтрацию по типам событий, по местам или по конкретным пользователям.

Рис. 6.48. Вкладка Audit log

GitHub-сценарии
Итак, вы познакомились с основными функциональными особенностями и рабочими схемами сайта GitHub, но в любой крупной группе проектов периодически
возникает необходимость сделать какие-то настройки или добавить к ней какие-то
внешние службы.
К счастью для нас, GitHub допускает разного рода вмешательства в свой код. В этом
разделе мы поговорим о работе с GitHub-системой хуков и API этого сайта. Именно
эти вещи позволяют добиться нужных нам результатов.

GitHub-сценарии   211

Хуки
Раздел управления хуками и службами, относящийся к администрированию
GitHub-репозиториев, является самым простым инструментом, чтобы наладить
взаимодействие сайта GitHub с внешними системами.

Службы
Начнем мы со служб. Интеграция хуков и служб осуществляется в разделе Settings
вашего репозитория, которым вы уже пользовались для добавления соавторов и изменения ветки, предлагаемой по умолчанию. Вам на выбор предоставляются десятки
служб, большая часть которых осуществляет интеграцию в другие коммерческие
системы и системы с открытым исходным кодом. По большей части это службы
непрерывной интеграции, системы слежения за ошибками и проблемами, системы
чатов и системы документирования. Рассмотрим процесс настройки на простом
примере. Выбрав в раскрывающемся списке Add Service вариант email, вы попадете
на экран, показанный на рис. 6.49.

Рис. 6.49. Конфигурирование службы электронной почты

В данном случае щелчок на кнопке Add service приведет к тому, что каждый раз
при добавлении кем-то данных в наш репозиторий нам будет приходить сообщение. Службы могут слушать события множества типов, но чаще всего они отсле­
живают события добавления и выполняют с полученными данными какие-то
действия.
Если вы хотите осуществить интеграцию какой-то системы с GitHub, первым делом
проверьте наличие готовой службы интеграции. К примеру, если для тестирования
на своей базе кода вы пользуетесь системой Jenkins, можно включить встроенный
режим интеграции с этой системой, и она будет запускаться для любого добавляемого в ваш репозиторий кода.

212    Глава 6 • GitHub

Хуки
Если вам требуется что-то более специфическое или служба, с которой вы хотите
выполнить интеграцию, отсутствует в списке, можно прибегнуть к более универсальной системе хуков. Хуки, связанные с GitHub-репозиторием, крайне просты.
Вы указываете адрес URL, а GitHub по протоколу HTTP отправляет на этот адрес
полезные данные для любого нужного вам события.
В общем случае вы настраиваете небольшую веб-службу, слушающую информацию
от GitHub-хука и затем совершающую какие-то действия с полученными данными.
Для включения хука щелкните на кнопке Add webhook, как показано на рис. 6.49.
Вы окажетесь на странице, представленной на рис. 6.50.

Рис. 6.50. Выбор конфигурации хука

Конфигурационные настройки в данном случае очень просты. В большинстве случаев достаточно указать URL-адрес и секретный ключ, а затем щелкнуть на кнопке
Add webhook. Возможны разные варианты событий, в случае которых вы захотите
получать данные от GitHub, — по умолчанию это событие push, то есть добавление
нового кода в любую ветку вашего репозитория.
Вот небольшой пример веб-службы, которую можно настроить для обработки хука. Мы воспользуемся фреймворком Sinatra на языке Ruby, так как он

GitHub-сценарии   213

в достаточной степени интуитивно понятен и позволяет легко посмотреть, что
именно мы делаем.
Предположим, мы хотим получать письмо, когда определенный человек отправляет
код в определенную ветку нашего проекта, редактируя определенный файл. В этом
нам поможет следующий код:
require 'sinatra'
require 'json'
require 'mail'
post '/payload' do
push = JSON.parse(request.body.read) # parse the JSON
# сбор данных, которые нам нужны
pusher = push["pusher"]["name"]
branch = push["ref"]
# получение списка всех затронутых файлов
files = push["commits"].map do |commit|
commit['added'] + commit['modified'] + commit['removed']
end
files = files.flatten.uniq
# проверка по заданным критериям
if pusher == 'schacon' &&
branch == 'ref/heads/special-branch' &&
files.include?('special-file.txt')
Mail.deliver do
from
'tchacon@example.com'
to
'tchacon@example.com'
subject 'Scott Changed the File'
body
"ALARM"
end
end
end

Здесь мы берем получаемые от GitHub данные в формате JSON и смотрим, кто их
автор, в какую ветку они были оправлены, какие файлы оказались затронутыми
присланными коммитами. Затем мы проверяем заданные критерии и посылаем
сообщение в случае совпадения.
Для разработки и тестирования подобных вещей потребуется консоль разработчика
на том же экране, где вы настраиваете хук. Вы можете посмотреть несколько последних доставок, которые для этого хука попытался сделать сайт GitHub. Для каждого хука можно посмотреть время доставки данных, успешность ее прохождения,
а также тело и заголовки запроса и ответа. Все это сильно упрощает тестирование
и отладку хуков (рис. 6.51).

214    Глава 6 • GitHub

Рис. 6.51. Отладочная информация для хуков

Другой замечательной возможностью является возможность повторной доставки
любых данных, полезная при тестировании службы.
Дополнительную информацию о написании хуков и типах доступных для прослушивания событий вы найдете в документации GitHub-разработчика по адресу:
https://developer.github.com/webhooks/.

API для GitHub
Службы и хуки уведомляют о событиях, происходящих в ваших хранилищах.
Но как быть, если требуется более подробная информация? Что делать, если вы
хотите автоматизировать такие действия, как добавление соавторов или пометка
проблем?

GitHub-сценарии   215

Здесь вам на помощь приходит API для GitHub. На сайте GitHub вы найдете
множество прикладных программных интерфейсов (Applications Programming
Interface, API), автоматизирующих практически любые действия. В этом разделе мы поговорим о способах аутентификации и подключения к API, а также
о способах добавления комментариев к проблемам и изменения статуса запроса
на включение через API.

Основы работы
Базовой вещью является простой запрос GET к конечной точке, не требующей
аутентификации. Это могут быть сведения о пользователе или предназначенная
только для чтения информация из проекта с открытым исходным кодом. Вот пример получения сведений о пользователе schacon:
$ curl https://api.github.com/users/schacon
{
«login»: «schacon»,
«id»: 70,
«avatar_url»: «https://avatars.githubusercontent.com/u/70»,
# ...
"name": "Scott Chacon",
"company": "GitHub",
"following": 19,
"created_at": "2008-01-27T17:19:28Z",
"updated_at": "2014-06-10T02:37:23Z"
}

Существуют миллионы аналогичных конечных точек, предоставляющих данные
об организациях, проектах, проблемах, коммитах — практически обо всем, что находится в открытом доступе на сайте GitHub. Воспользоваться API можно даже
для визуализации произвольной разметки на языке Markdown или поиска шаблона
.gitignore.
$ curl https://api.github.com/gitignore/templates/Java
{
"name": "Java",
"source": "*.class
# Мобильные инструменты для Java (J2ME)
.mtj.tmp/
# Пакетные файлы #
*.jar
*.war
*.ear

}

# просмотр журнала аварии виртуальной машины http://www.java.com/en/download/
help/error_hotspot.xml
hs_err_pid*
"

216    Глава 6 • GitHub

Комментирование проблем
Однако для различных действий на сайте, таких как комментирование проблемы
или запроса на включение либо получение закрытых данных, вам потребуется
аутентификация.
Ее можно выполнить разными способами. Можно ограничиться базовой аутентификацией по имени пользователя и паролю, но в общем случае лучше воспользоваться личным маркером доступа. Он генерируется на вкладке Applications страницы
настройки (рис. 6.52).

Рис. 6.52. Генерация маркера доступа на вкладке Applications
страницы настройки

Вас попросят указать сферу действия этого маркера и дать его описание. Постарайтесь сделать описание таким, чтобы по нему можно было легко найти маркер,
когда надобность в нем отпадет и потребуется его удалить.
Сайт GitHub показывает маркер всего один раз, поэтому не забудьте его скопи­
ровать. После этого вы можете указывать его вместо имени пользователя и
па­роля для ­аутентификации в ваших сценариях. Это очень удобно, так как вы
може­те ограничить диапазон своих действий, а маркер в любой момент можно
уничтожить.
Кроме того, вы получаете дополнительное преимущество в виде повышения быстродействия. Без аутентификации вы можете делать всего 60 запросов в час, а после
ее прохождения количество запросов в час возрастает до 5000.

GitHub-сценарии   217

Давайте посмотрим на процедуру добавления комментария к одной из наших проблем. К примеру, мы хотим прокомментировать проблему номер 6. Для этого нам
потребуется сделать HTTP-запрос POST к странице repos///issues//comments с только что сгенерированным как заголовок
Authorization маркером.
$ curl -H "Content-Type: application/json" \
-H "Authorization: token TOKEN" \
--data '{"body":"A new comment, :+1:"}' \
https://api.github.com/repos/schacon/blink/issues/6/comments
{
"id": 58322100,
"html_url": "https://github.com/schacon/blink/issues/6#issuecomment-58322100",
...
"user": {
"login": "tonychacon",
"id": 7874698,
"avatar_url": "https://avatars.githubusercontent.com/u/7874698?v=2",
"type": "User",
},
"created_at": "2014-10-08T07:48:19Z",
"updated_at": "2014-10-08T07:48:19Z",
"body": "A new comment, :+1:"
}

Если теперь посмотреть на цепочку обсуждения данной проблемы, вы увидите
только что отправленный комментарий (рис. 6.53).

Рис. 6.53. Комментарий, отправленный посредством API для GitHub

Применять API можно практически для любых операций на сайте: создания и настройки контрольных точек, связывания пользователей с проблемами и запросами на включение, создания и редактирования меток, доступа к данным коммита,
создания новых коммитов и веток, открытия, закрытия и слияния запросов на
включение, создания и редактирования групп, комментирования строк кода в запро­
се на включение, поиска по сайту и многого другого.

Изменение статуса запроса на включение
Вот еще один последний пример, который мы рассмотрим в этой главе, так как
он может пригодиться вам при работе с запросами на включение. С каждым коммитом может быть связано одно или несколько состояний, поэтому существует

218    Глава 6 • GitHub
прикладной программный интерфейс, позволяющий добавлять состояние или
делать к нему запрос.
К этому интерфейсу прибегает большинство служб непрерывной интеграции и
тестирования с целью проверки присланного кода и формирования отчета в случае,
если коммит прошел все тесты. Вы можете воспользоваться им, к примеру, чтобы
проверить корректность форматирования сообщения фиксации, следование автора
коммита вашим пожеланиям, правильность подписи коммита — словом, выполнить
любые проверки.
Предположим, вы настроили для своего репозитория хук, связывающийся с компактной веб-службой, которая проверяет строку Signed-off-by в сообщении фиксации.
require 'httparty'
require 'sinatra'
require 'json'
post '/payload' do
push = JSON.parse(request.body.read) # parse the JSON
repo_name = push['repository']['full_name']
# просмотр всех сообщений фиксации
push["commits"].each do |commit|
# поиск строки Signed-off-by
if /Signed-off-by/.match commit['message']
state = 'success'
description = 'Successfully signed off!'
else
state = 'failure'
description = 'No signoff found.'
end
# отправка состояния на сайт GitHub
sha = commit["id"]
status_url = "https://api.github.com/repos/#{repo_name}/statuses/#{sha}"

end

status = {
"state" => state,
"description" => description,
"target_url" => "http://example.com/how-to-signoff",
"context" => "validate/signoff"
}
HTTParty.post(status_url,
:body => status.to_json,
:headers => {
'Content-Type' => 'application/json',
'User-Agent' => 'tonychacon/signoff',
'Authorization' => "token #{ENV['TOKEN']}" }
)
end

GitHub-сценарии   219

К счастью, понять этот код просто. В данном обработчике хука мы просматриваем
все только что присланные коммиты, ищем в их сообщениях фиксации строку
Signed-off-by и выполняем HTTP-запрос POST к странице /repos///statuses/ конечной точки API с данными
о состоянии.
В данном случае можно послать данные состояния (success, failure, error),
­описание происшедшего, URL-адрес страницы, на которой пользователь может
получить дополнительную информацию, а в случае, если с одним коммитом связаны
несколько состояний, еще и контекст. Ведь отчет о состоянии может послать как
служба тестирования, так и служба проверки, подобная описанной; различаться
они будут только контекстом.
Если при наличии этого хука кто-то откроет на сайте GitHub новый запрос на
включение, может возникнуть ситуация, показанная на рис. 6.54.

Рис. 6.54. Состояние коммита, полученное через API

Обратите внимание на галочку рядом с коммитом, у которого есть строка Signedoff-by , и крестик рядом с другим коммитом, автор которого забыл добавить
­подпись. При этом запрос на включение принимает состояние последнего коммита в ветке и выводит предостережение в случае, когда его слияние невозможно. Крайне­рекомендуем вам пользоваться этим API для результатов тестирования, чтобы избежать­случайного слияния с коммитом, не прошедшим
тестирование.

220    Глава 6 • GitHub

От пользователя Octokit
Хотя в данной главе мы рассмотрели практически все, что касается сложных
и простых­HTTP-запросов, существует несколько библиотек открытого исходного
кода, позволяющих работать с этим API в более свободном стиле. На момент написания данной книги поддерживались такие языки, как Go, Objective-C, Ruby
и .NET. Дополнительную информацию об этом можно получить на странице
­ ttp://github.com/octokit.
h
Надеемся, что эти инструменты помогут вам настроить сайт GitHub, оптимизировав
его под собственную схему работы. Полная документация на API и руководство по
решению типовых задач находятся на странице https://developer.github.com.

Заключение
Теперь вы — пользователь сайта GitHub. Вы умеете создавать учетные записи,
руководить организацией, создавать и обновлять репозитории, вносить вклад
в чужие проекты и принимать вклады от других пользователей. В следующей главе
вы познакомитесь с еще более мощными инструментами и найдете ряд советов, помогающих отыскать решение в сложных ситуациях. Все это сделает вас настоящим
экспертом по Git.

7

Git-инструментарий

К настоящему моменту вы уже изучили большинство необходимых для повседневной работы команд и рабочих схем, используемых для управления Git-репозиторием
и исходным кодом. Вы решили ряд задач, связанных с файлами слежения и фиксации состояния, а также ощутили на собственном опыте, насколько мощными
инструментами являются область индексирования и облегченные процедуры
ветвления и слияния.
Пришла пора детально рассмотреть ряд высокопроизводительных операций, которые, возможно, не потребуются вам для решения рутинных задач, но в какой-то
момент могут пригодиться.

Выбор версии
В Git есть несколько способов выбора определенных коммитов или диапазона
коммитов. Далеко не все из них очевидны, но их следует знать.

Одна версия
Понятно, что обращение к коммиту можно выполнить через его контрольную
сумму SHA-1, но существуют и более удобные для восприятия варианты. В этом
разделе мы перечислим различные способы ссылки на отдельный коммит.

222    Глава 7 • Git-инструментарий

Короткий вариант SHA
Система Git достаточно умна, чтобы по нескольким первым символам хеша распознать, какой коммит вы имеете в виду. Главное, чтобы частичная контрольная
сумма SHA-1 состояла хотя бы из четырех символов и была уникальной, — то есть
в текущем репозитории ей должен соответствовать всего один объект.
Предположим, вы запускаете команду git log и находите коммит, в который вы
добавили какие-то конструктивные особенности:
$ git log
commit 734713bc047d87bf7eac9674765ae793478c50d3
Author: Scott Chacon
Date:
Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon
Date:
Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon
Date:
Thu Dec 11 14:58:32 2008 -0800
added some blame and merge stuff

В данном случае выберем коммит 1c002dd.... Если теперь воспользоваться командой git show для разных вариантов написания контрольной суммы, то результат
во всех показанных далее случаях будет одинаковым (разумеется, при условии,
что минимальная длина контрольной суммы соответствует единственной версии):
$ git show 1c002dd4b536e7479fe34593e72e6c6c1819e53b
$ git show 1c002dd4b536e7479f
$ git show 1c002d

Для ваших значений SHA-1 Git может представить короткие, уникальные сокращения. Добавив к команде git log параметр --abbrev-commit, вы получите набор
сокращенных и вместе с тем уникальных значений; по умолчанию длина составляет
семь символов, но при необходимости она увеличивается до размера, обеспечивающего уникальность контрольной суммы SHA-1:
$ git log --abbrev-commit --pretty=oneline
ca82a6d changed the version number
085bb3b removed unnecessary test code
a11bef0 first commit

В общем случае для уникальности в рамках проекта достаточно указать от восьми
до десяти символов.

Выбор версии   223

К примеру, в таком крупном проекте, как ядро Linux с его более 450 тысячами коммитов и 3,6 миллиона объектов, не существуют двух объектов, контрольная сумма
SHA которых совпадала бы более чем в первых 11 символах.
ПРИМЕЧАНИЕ ПО ПОВОДУ SHA-1
Многие пользователи озабочены тем, что в какой-то момент в их репозитории могут
оказаться два объекта с одинаковым значением SHA-1. Что произойдет в этом случае?
Предположим, вы пытаетесь выполнить фиксацию объекта, хеш SHA-1 которого совпадает с хешем другого объекта, уже существующего в данном репозитории. Система Git
обнаружит этот предыдущий объект в базе данных, и с ее точки зрения он уже будет
записан. В результате при будущих проверках вы все время будете получать данные
первого объекта.
Другое дело, что подобный сценарий практически невероятен. Размер хеш-суммы
SHA-1 составляет 20 байт, или 160 бит. Количество случайным образом хешированных
­объектов, необходимое для гарантирования 50-процентной вероятности единственного
совпадения, составляет примерно 280 (вот формула расчета вероятности совпадения
p = (n(n – 1)/2) × (1/2160)). Это 1,2 × 1024, или более триллиона триллионов вариантов,
что в 1200 раз больше количества песчинок на земле.
Вот пример, демонстрирующий, насколько невероятной вещью является совпадение
значений SHA-1. Если бы все 6,5 миллиарда жителей нашей планеты занимались программированием, каждую секунду каждый из них производил бы количество кода,
равное коду, написанному за всю историю существования ядра Linux (а это 3,6 миллиона
Git-объектов), и отправлял бы этот код в один огромный Git-репозиторий, потребовалось бы почти 2 года, чтобы число объектов в этом репозитории достигло величины,
необходимой для обеспечения 50-процентной вероятности совпадения SHA-1 у пары
объектов. Куда выше вероятность того, что два члена рабочей группы будут одновременно, но независимо друг от друга съедены волками.

Ссылки из веток
Самый простой способ выбора определенного коммита требует наличия ссылки на
этот коммит из ветки. В этом случае в любой команде, где требуется указать объект-коммит, можно будет использовать имя ветки или значение SHA-1. Например,
если ветка topic1 ссылается на коммит ca82a6d, просмотр последнего коммита
в ветке можно осуществить двумя командами:
$ git show ca82a6dff817ec66f44342007202690a93763949
$ git show topic1

Посмотреть значение SHA, на которое ссылается ветка, или понять, как выглядят
приведенные примеры в терминах SHA, позволяет служебная Git-программа revparse. Служебные программы будут подробно рассматриваться в главе 10; а пока
скажем только, что эта программа предназначена для низкоуровневых операций
и в повседневной работе к ней прибегают не часто. Но в ситуациях, когда нужно

224    Глава 7 • Git-инструментарий
понять, что именно происходит, она незаменима. Вот результат ее применения
к вашей ветке.
$ git rev-parse topic1
ca82a6dff817ec66f44342007202690a93763949

Сокращения журнала ссылок
Одной из вещей, которую Git делает в фоновом режиме, является ведение журнала
ссылок, в котором хранятся ссылки указателей HEAD и веток за последние несколько
месяцев.
Для просмотра этого журнала используется команда git reflog:
$ git reflog
734713b... HEAD@{0}:
d921970... HEAD@{1}:
1c002dd... HEAD@{2}:
1c36188... HEAD@{3}:
95df984... HEAD@{4}:
1c36188... HEAD@{5}:
7e05da5... HEAD@{6}:

commit: fixed refs handling, added gc auto, updated
merge phedders/rdocs: Merge made by recursive.
commit: added some blame and merge stuff
rebase -i (squash): updating HEAD
commit: # This is a combination of two commits.
rebase -i (squash): updating HEAD
rebase -i (pick): updating HEAD

В виде этой временной истории Git сохраняет данные о каждом изменении вершины
ветки. Позволяют эти данные указывать и на более старые коммиты. Например,
чтобы посмотреть, куда ссылался указатель HEAD пять шагов назад, используйте
ссылку @{n}, которую можно увидеть в выводимых данных команды reflog:
$ git show HEAD@{5}

Этот синтаксис используется и в случае, когда требуется посмотреть, в каком состоянии пребывала ветка некоторое время назад. Например, чтобы увидеть, как
выглядела ветка вчера, следует написать:
$ git show master@{yesterday}

Вы увидите, что было на вершине ветки вчера. Этот способ работает только для
данных, которые все еще находятся в вашем журнале ссылок, поэтому его невозможно использовать для просмотра коммитов, возраст которых превышает несколько месяцев.
Для просмотра информации из журнала ссылок в таком же формате, как в выводимых данных команды git log, используйте вариант git log -g:
$ git log -g master
commit 734713bc047d87bf7eac9674765ae793478c50d3
Reflog: master@{0} (Scott Chacon )
Reflog message: commit: fixed refs handling, added gc auto, updated
Author: Scott Chacon
Date:
Fri Jan 2 18:32:33 2009 -0800
fixed refs handling, added gc auto, updated tests

Выбор версии   225
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Reflog: master@{1} (Scott Chacon )
Reflog message: merge phedders/rdocs: Merge made by recursive.
Author: Scott Chacon
Date:
Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'

Важно понимать, что выводимые этой командой данные касаются исключительно
локальной информации, — это журнал событий вашего репозитория. Для чужой
копии вашего репозитория ссылки будут уже другими; а сразу после клонирования
журнал окажется пустым, так как в репозитории еще не совершалось никаких операций. Команда git show HEAD@{2.months.ago} даст результат только в случае, когда
проект клонирован более двух месяцев назад, — если вы выполнили клонирование
пять минут назад, результата не будет.

Ссылки на предков
Указать коммит можно и через его родителя. Символ ^ в конце ссылки с точки
зрения Git соответствует предку коммита. Предположим, у вас есть проект со
следующей историей:
$ git log --pretty=format:'%h %s' --graph
* 734713b fixed refs handling, added gc auto, updated tests
* d921970 Merge commit 'phedders/rdocs'
|\
| * 35cfb2b Some rdoc changes
* | 1c002dd added some blame and merge stuff
|/
* 1c36188 ignore *.gem
* 9b29157 add open3_detach to gemspec file list

Для просмотра предыдущего коммита достаточно написать HEAD^, что означает
«родитель HEAD».
$ git show HEAD^
commit d921970aadf03b3cf0e71becdaab3147ba71cdef
Merge: 1c002dd... 35cfb2b...
Author: Scott Chacon
Date:
Thu Dec 11 15:08:43 2008 -0800
Merge commit 'phedders/rdocs'

После символа ^ можно указать число: например, запись d921970^2 означает «второй
предок коммита d921970». Этот синтаксис применяется только в случае коммитов
слияния, у которых существует несколько предков. Первый родитель — это ветка,
на которой вы находились в момент слияния, а второй — коммит на ветке, которая
подверглась слиянию:
$ git show d921970^
commit 1c002dd4b536e7479fe34593e72e6c6c1819e53b
Author: Scott Chacon

226    Глава 7 • Git-инструментарий
Date:

Thu Dec 11 14:58:32 2008 -0800

added some blame and merge stuff
$ git show d921970^2
commit 35cfb2b795a55793d7cc56a6cc2060b4bb732548
Author: Paul Hedderly
Date:
Wed Dec 10 22:22:03 2008 +0000
Some rdoc changes

Другое распространенное обозначение предка — символ ~. Он также соответствует
ссылке на первого родителя, поэтому записи HEAD~ и HEAD^ эквивалентны. Различия
проявляются, когда вы указываете номер. Запись HEAD~2 означает «первый предок
первого предка», то есть фактически «дедушка», — при этом происходит переход
от заданного предка вглубь указанное число раз. К примеру, для показанной ранее
истории HEAD~3 дает следующий результат:
$ git show HEAD~3
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner
Date:
Fri Nov 7 13:47:59 2008 -0500
ignore *.gem

Еще это можно записать как HEAD^^^, что опять же означает первого предка первого
предка первого предка:
$ git show HEAD^^^
commit 1c3618887afb5fbcbea25b7c013f4e2114448b8d
Author: Tom Preston-Werner
Date:
Fri Nov 7 13:47:59 2008 -0500
ignore *.gem

Указанные обозначения можно комбинировать. К примеру, второго родителя предыдущей ссылки (при условии, что это коммит слияния) можно получить, написав
HEAD~3^2.

Диапазоны коммитов
Теперь, когда вы научились ссылаться на отдельные коммиты, разберемся с процедурой ссылки на диапазон коммитов. Это часто нужно при управлении ветками: если веток много, именно ссылка на диапазон отвечает на такой вопрос, как,
к примеру: «Какие наработки из этой ветки еще не были слиты в основную ветку?»

Две точки
Чаще всего диапазон коммитов обозначают двумя точками. По сути, так вы
­обращаетесь к Git с командой определить диапазон коммитов, достижимый из

Выбор версии   227

одного коммита, но недостижимый из другого. Рассмотрим историю коммитов
с рис. 7.1.
A

B

E

F

master

C

D

experiment

Рис. 7.1. История коммитов, на примере которой мы будем указывать диапазон

Предположим, нам нужно определить, какая информация из ветки experiment
пока не слита в ветку master. Чтобы система Git показала вам только список таких
коммитов, следует написать master..experiment — это означает «все коммиты из
ветки experiment, отсутствующие в ветке master». Для краткости и наглядности в
рассматриваемых примерах мы будем указывать на диаграммах не реальные данные,
выводимые командой log, а буквы в порядке их появления:
$ git log master..experiment
D
C

Чтобы рассмотреть обратную ситуацию — все коммиты из ветки master, отсутствующие в ветке experiment, — достаточно поменять порядок следования веток.
Список таких коммитов обеспечит вам запись experiment..master:
$ git log experiment..master
F
E

Это пригодится, когда требуется поддерживать ветку experiment в актуальном
состоянии и осуществлять предварительный просмотр данных, которые вы собираетесь в нее слить. Еще данный синтаксис часто используется для просмотра
информации, которую вы собираетесь отправить на удаленный сервер:
$ git log origin/master..HEAD

Эта команда покажет вам все коммиты в текущей ветке, отсутствующие в ветке
master на сервере origin. При запуске команды git push из ветки, которая следит
за веткой origin/master, на сервер будут переданы именно коммиты, фигурирующие в выводимых командой git log origin/master..HEAD данных. Если опустить
одну из частей записи, Git подставит туда HEAD. К примеру, команда git log origin/
master.. дает такой же результат, как и показанная ранее команда, потому что
вместо пропущенного фрагмента Git подставляет HEAD.

Множественная выборка
Запись с двумя точками полезна как сокращенный вариант. Но порой, чтобы указать нужный вам коммит, требуется перечислить более двух веток, — например,

228    Глава 7 • Git-инструментарий
для просмотра коммитов из веток, отсутствующих в той ветке, в которой вы находитесь. В Git это достигается при помощи символа ^ или параметра --not перед
именем ветки, коммиты из которой вы не хотите видеть. Следующие три команды
эквивалентны:
$ git log refA..refB
$ git log ^refA refB
$ git log refB --not refA

Этот синтаксис удобен тем, что позволяет указать в запросе более двух ссылок,
что невозможно в синтаксисе с двумя точками. Например, если вы хотите увидеть
все коммиты, достижимые по ссылке refA или refB, но недостижимые по ссылке
refC, можно написать так:
$ git log refA refB ^refC
$ git log refA refB --not refC

Это позволяет получить очень мощную систему запросов на изменение, призванную
помочь вам выяснить реальную ситуацию с ветками.

Три точки
Последний вариант записи для выбора диапазона коммитов — три точки, обозначающие, что нас интересуют коммиты, достижимые по одной из двух ссылок, но не
по обеим одновременно. Вернемся к рассмотренному примеру. Чтобы посмотреть
коммиты, которые находятся только в ветке master или только в ветке experiment,
но не в обеих ветках одновременно, можно написать:
$ git log master...experiment
F
E
D
C

Еще раз напомним, что эта команда даст нам стандартный вывод, просто в нем
будет содержаться информация только об этих четырех коммитах, как обычно,
упорядоченных по дате создания.
С этой командой часто используют параметр --left-right, позволяющий посмотреть, с какой стороны диапазона находится каждый коммит. Это увеличивает
практическую ценность данных:
$
<
<
>
>

git log --left-right master...experiment
F
E
D
C

Эти инструменты дают вам возможность объяснить системе Git, какой коммит или
коммиты вы хотите изучить.

Интерактивное индексирование   229

Интерактивное индексирование
Вместе с Git поставляется пара сценариев, облегчающих решение некоторых
задач из командной строки. В этом разделе мы рассмотрим несколько интерактивных команд, позволяющих включать в коммиты только определенные
комбинации или части файлов. Это полезно в случаях, когда результат редактирования файлов вы хотите сохранить в нескольких коммитах, а не в одном
большом. Такой подход гарантирует, что ваши коммиты будут представлены
как логически ­разделенные наборы изменений, которые легко задействовать сотрудничающим с вами разработчикам. Добавление к команде git add параметра
-i или --interactive переводит Git в режим интерактивной оболочки и дает
примерно такой результат:
$ git add -i
staged unstaged path
1: unchanged
+0/-1 TODO
2: unchanged
+1/-1 index.html
3: unchanged
+5/-1 lib/simplegit.rb
*** Commands ***
1: status
2: update
5: patch
6: diff
What now>

3: revert
7: quit

4: add untracked
8: help

Фактически эта команда показывает содержимое области индексации — по сути, эту
информацию вы получили бы и при помощи команды git status, просто в данном
случае она представлена в более сжатом и информативном виде. Индексированные
изменения отображаются слева, а неиндексированные — справа.
Следом идет раздел Commands. Здесь можно выполнить ряд операций, включая
индексирование файлов, отмену этой операции, индексирование частей файлов,
добавление неотслеживаемых файлов и просмотр новой информации, которая
подверглась индексированию.

Индексирование файлов и его отмена
Если ввести 2 или u в ответ на приглашение What now>, сценарий спросит, какие
файлы вы хотите проиндексировать:
What now> 2
staged unstaged path
1: unchanged
+0/-1 TODO
2: unchanged
+1/-1 index.html
3: unchanged
+5/-1 lib/simplegit.rb
Update>>

230    Глава 7 • Git-инструментарий
Для индексации файлов TODO и index.html достаточно указать их номера:
Update>> 1,2
staged unstaged path
* 1: unchanged
+0/-1 TODO
* 2: unchanged
+1/-1 index.html
3: unchanged
+5/-1 lib/simplegit.rb
Update>>

Символ * рядом с именем файла означает, что он выбран для индексирования. Если,
ничего не добавляя, нажать клавишу Enter в ответ на приглашение Update>>, Git
возьмет все выбранные на данный момент файлы и проиндексирует их:
Update>>
updated 2 paths
*** Commands ***
1: status
2: update 3:
5: patch
6: diff
7:
What now> 1
staged
unstaged
1:
+0/-1
nothing
2:
+1/-1
nothing
3: unchanged
+5/-1

revert
quit

4: add untracked
8: help

path
TODO
index.html
lib/simplegit.rb

Как видите, теперь файлы TODO и index.html проиндексированы, а файл simplegit.
rb — пока нет. Если в этот момент у вас появится желание отменить индексирование файла TODO, воспользуйтесь параметром 3 или r (от слова revert — возвращать
в первоначальное состояние):
*** Commands ***
1: status
2: update
3: revert
4: add untracked
5: patch
6: diff
7: quit
8: help
What now> 3
staged
unstaged path
1:
+0/-1
nothing TODO
2:
+1/-1
nothing index.html
3: unchanged
+5/-1 lib/simplegit.rb
Revert>> 1
staged unstaged path
* 1:
+0/-1
nothing TODO
2:
+1/-1
nothing index.html
3: unchanged
+5/-1 lib/simplegit.rb
Revert>> [enter]
reverted one path

Если сейчас посмотреть на состояние Git, вы обнаружите, что файл TODO удален
из области индексирования:
*** Commands ***
1: status
2:
update 3: revert
4: add untracked
5: patch
6:
diff 7: quit
8: help
What now> 1
staged unstaged path
1: unchanged
+0/-1 TODO
2:
+1/-1
nothing index.html
3: unchanged
+5/-1 lib/simplegit.rb

Интерактивное индексирование   231

Чтобы посмотреть, какие же изменения вы проиндексировали, используйте коман­
ду 6 или d (от слова diff — различия). Вы получите список индексированных файлов,
и останется указать, вносимые какими файлами изменения вас интересуют. Это
аналог ввода в командной строке команды git diff --cached:
*** Commands ***
1: status
2: update
3: revert
4: add untracked
5: patch
6: diff
7: quit
8: help
What now> 6
staged
unstaged path
1:
+1/-1
nothing index.html
Review diff>> 1
diff --git a/index.html b/index.html
index 4d07108..4335f49 100644
--- a/index.html
+++ b/index.html
@@ -16,7 +16,7 @@ Date Finder
...
-contact : support@github.com
+contact : email.support@github.com


Эти базовые команды позволяют добавлять файлы в область индексирования
в интерактивном режиме, несколько упрощая вам жизнь.

Индексирование изменений
Еще Git позволяет индексировать только отдельные части файлов. К примеру, если
вы внесли в файл simplegit.rb два изменения и хотите проиндексировать только
одно из них, делается это очень просто. Введите в строку приглашения интерактивной оболочки параметр 5 или p (от слова patch — исправление). Вас спросят, какие
файлы вы собираетесь индексировать частями; затем для каждой части выбранного
файла будут по очереди отображаться добавленные изменения с вопросом, хотите
ли вы их индексировать:
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index dd5ecc4..57399e0 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -22,7 +22,7 @@ class SimpleGit
end
+

def log(treeish = 'master')
command("git log -n 25 #{treeish}")
command("git log -n 30 #{treeish}")
end

def blame(path)
Stage this hunk [y,n,a,d,/,j,J,g,e,?]?

232    Глава 7 • Git-инструментарий
Как видите, у вас есть множество вариантов действий. Ввод символа ? позволяет
увидеть перечень доступных вам вариантов:
Stage this hunk [y,n,a,d,/,j,J,g,e,?]? ?
y - stage this hunk
n - do not stage this hunk
a - stage this and all the remaining hunks in the file
d - do not stage this hunk nor any of the remaining hunks in the file
g - select a hunk to go to
/ - search for a hunk matching the given regex
j - leave this hunk undecided, see next undecided hunk
J - leave this hunk undecided, see next hunk
k - leave this hunk undecided, see previous undecided hunk
K - leave this hunk undecided, see previous hunk
s - split the current hunk into smaller hunks
e - manually edit the current hunk
? - print help

Как правило, самыми востребованными оказываются параметры y и n, указывающие, хотите вы или нет индексировать конкретный фрагмент, но иногда требуется
индексировать несколько фрагментов вместе или отложить решение на потом. После
частичного индексирования файла информация о его состоянии будет выглядеть так:
What now> 1
1:
2:
3:

staged
unchanged
+1/-1
+1/-1

unstaged
+0/-1
nothing
+4/-0

path
TODO
index.html
lib/simplegit.rb

Посмотрите, как интересно выглядит информация о состоянии файла simplegit.
rb. Она показывает, что пара строчек проиндексирована, а вторая пара — нет. То
есть вы частично проиндексировали этот файл. Теперь можно завершить работу
с интерактивным сценарием и запустить команду git commit для фиксации состояния частично индексированных файлов.
Впрочем, для частичной индексации файлов вам не обязательно переходить в режим интерактивного добавления — для запуска этого сценария достаточно набрать
в командной строке команду git add -p или git add --patch.
Режим работы с фрагментами файлов также может применяться для частичного восстановления файлов командой reset --patch, для перехода к нужному фрагменту
при помощи команды checkout --patch и для скрытия частей файлов командой
stash save --patch. Подробно эти команды будут описываться при рассмотрении
более специализированных вариантов использования.

Скрытие и очистка
Часто во время работы над проектом, когда все еще находится в беспорядочном
состоянии, возникает необходимость перейти в другую ветку и поработать над

Скрытие и очистка   233

другим аспектом. Проблема в том, что фиксировать работу, сделанную наполовину,
чтобы позже к ней вернуться, вы не хотите. В такой ситуации вам на помощь придет команда git stash.
При ее выполнении все содержимое рабочей папки — то есть отредактированные
отслеживаемые файлы и индексированные изменения — сохраняется в стеке незавершенных изменений, откуда вы их можете достать в любой удобный для вас
момент.

Скрытие вашей работы
Чтобы посмотреть, как это работает, отредактируйте пару файлов своего проекта
и проиндексируйте один из них. Команда git status покажет, что состояние проекта изменилось:
$ git status
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: index.html
Changes not staged for commit:
(use "git add ..." to update what will be committed)
(use "git checkout -- ..." to discard changes in working directory)
modified: lib/simplegit.rb

Если теперь вы решите перейти в другую ветку, не фиксируя результатов своей
работы, их можно просто скрыть. Спрятать все в буфер позволяет команда git
stash или git stash save:
$ git stash
Saved working directory and index state \
"WIP on master: 049d078 added the index file"
HEAD is now at 049d078 added the index file
(To restore them type "git stash apply")

В результате рабочая папка оказывается очищенной:
$ git status
# На ветке master
nothing to commit (working directory clean)

Теперь можно легко менять ветки и работать над другим фрагментом проекта —
все изменения хранятся в стеке. Увидеть содержимое стека позволяет команда git
stash list:
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log

В нашем случае операция скрытия уже два раза выполнялась, поэтому в буфере
хранится три варианта наработок. Последняя строка вывода исходной команды

234    Глава 7 • Git-инструментарий
stash подсказывает нам, как вернуть спрятанные в буфер изменения в рабочее
состояние. Для этого используется команда git stash apply. Если же вы хотите

вернуться к работе над версией, сохраненной в буфер ранее, укажите ее номер, например git stash apply stash@{2}. Без указания номера Git возвращает в работу
последние сохраненные в буфере изменения:
$ git stash apply
# On branch master
# Changed but not updated:
# (use "git add ..." to update what will be committed)
#
#
modified: index.html
#
modified: lib/simplegit.rb
#

Как видите, система Git вернула в файлы все изменения, которые вы отменили, сохранив их в буфере. В данном случае мы возвращали содержимое буфера в чистый
рабочий каталог и в ту же ветку, из которой они были сохранены; но для успешного
возвращения состояния в работу эти условия обязательными не являются. Можно
скрыть изменения одной ветки, перейти на другую и попытаться вставить измененное состояние туда. Более того, на момент извлечения содержимого из буфера в
рабочей папке могут находиться отредактированные файлы с незафиксированными изменениями — если вставить данные без проблем не получится, Git сообщит
о конфликте слияния.
После извлечения информации из буфера файлы, которые до помещения в буфер
были проиндексированы, автоматически в это состояние не вернутся. Вам потребуется выполнить команду git stash apply с параметром --index, которая осуществит повторную индексацию. Чтобы сразу вернуть данные из буфера в исходное
состояние, нужно написать:
$
#
#
#
#
#
#
#
#
#
#
#

git stash apply --index
On branch master
Changes to be committed:
(use "git reset HEAD ..." to unstage)
modified: index.html
Changed but not updated:
(use "git add ..." to update what will be committed)
modified: lib/simplegit.rb

При этом команда apply только возвращает данные в ветке, но из стека они никуда не деваются. Убрать их из стека позволяет команда git stash drop с именем
удаляемого фрагмента:
$ git stash list
stash@{0}: WIP on master: 049d078 added the index file
stash@{1}: WIP on master: c264051... Revert "added file_size"
stash@{2}: WIP on master: 21d80a5... added number to log

Скрытие и очистка   235
$ git stash drop stash@{0}
Dropped stash@{0} (364e91f3f268f0900bc3ee613f9f733e82aaed43)

Впрочем, существует также команда git stash pop, которая возвращает сохраненную
в буфере информацию в ветку и немедленно удаляет ее из буфера.

Более сложные варианты скрытия
У команды stash существует ряд параметров, которые могут вам пригодиться.
Во-первых, достаточно популярен параметр --keep-index команды stash save.
Наличие этого параметра сообщает Git о том, что не нужно скрывать данные, проиндексированные командой git add.
Она применяется в случаях, когда вы хотите зафиксировать только часть внесенных
изменений, а остальные хотите сохранить, чтобы вернуться к работе с ними позже.
$ git status -s
M index.html
M lib/simplegit.rb
$ git stash --keep-index
Saved working directory and index state WIP on master: 1b65b17 added the index
file
HEAD is now at 1b65b17 added the index file
$ git status -s
M index.html

Также часто возникаетситуация, когда требуется скрыть неотслеживаемые файлы
вместе с отслеживаемыми. По умолчанию команда git stash сохраняет только
файлы из области индексирования. Но параметр --include-untracked или -u заставляет систему Git сохранять также все неотслеживаемые файлы.
$ git status -s
M index.html
M lib/simplegit.rb
?? new-file.txt
$ git stash -u
Saved working directory and index state WIP on master: 1b65b17 added the index
file
HEAD is now at 1b65b17 added the index file
$ git status -s
$

Наконец, флаг --patch приводит к тому, что Git вместо скрытия всех модифицированных файлов начинает спрашивать вас в интерактивном режиме, какие файлы
вы предпочитаете спрятать, а какие следует оставить в рабочей папке.
$ git stash --patch
diff --git a/lib/simplegit.rb b/lib/simplegit.rb

236    Глава 7 • Git-инструментарий
index 66d332e..8bb5674 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -16,6 +16,10 @@ class SimpleGit
return `#{git_cmd} 2>&1`.chomp
end
end
+
+
def show(treeish = 'master')
+
command("git show #{treeish}")
+
end
end
test
Stash this hunk [y,n,q,a,d,/,e,?]? y
Saved working directory and index state WIP on master: 1b65b17 added the index
file

Отмена скрытых изменений
Может возникнуть ситуация, когда после возвращения изменений из буфера
вы выполняете некую работу, а потом хотите отменить изменения, внесенные из
буфера. Команды stash unapply в Git нет, но нужный эффект можно получить,
если сначала извлечь связанные с буфером исправления, а затем применить их
в реверсивном виде:
$ git stash show -p stash@{0} | git apply -R

Еще раз напоминаем, что если номер версии не указан в явном виде, Git берет
фрагмент данных, скрытый последним:
$ git stash show -p | git apply -R

Существует возможность создать псевдоним и добавить в свою версию Git команду
stash-unapply. Например:
$
$
$
$

git config --global alias.stash-unapply '!git stash show -p | git apply -R'
git stash
#... work work work
git stash-unapply

Создание ветки из скрытого фрагмента
Если скрыть некие наработки, оставить их на некоторое время в буфере, а тем
временем продолжить работу в ветке, из которой была скрыта информация, в итоге
можно столкнуться с ситуацией, когда просто взять и вернуть данные из буфера
не удастся. Ситуация, когда возвращение данных сопровождается попыткой отредактировать файл, который с момента скрытия был модифицирован, приводит

Скрытие и очистка   237

к конфликту слияния, требующему вашего разрешения. Намного проще протестировать скрытые изменения командой git stash branch. Она создает новую
ветку, переходит к коммиту, в котором вы находились на момент скрытия работы, копирует в новую ветку содержимое буфера и очищает его, если изменения
прошли успешно:
$ git stash branch testchanges
Switched to a new branch "testchanges"
# On branch testchanges
# Changes to be committed:
# (use "git reset HEAD ..." to unstage)
#
#
modified: index.html
#
# Changed but not updated:
# (use "git add ..." to update what will be committed)
#
#
modified: lib/simplegit.rb
#
Dropped refs/stash@{0} (f0dfc4d5dc332d1cee34a634182e168c4efc3359)

Это удобный способ легко восстановить скрытые изменения и продолжить работу
с ними в новой ветке.

Очистка рабочей папки
В некоторых ситуациях лучше не скрывать результаты своего труда или файлы,
а избавиться от них. Это можно сделать командой git clean.
Удаление требуется, чтобы убрать мусор, сгенерированный путем слияний или
внешними инструментами, или чтобы избавиться от артефактов сборки в процессе
ее очистки.
С этой командой нужно быть крайне аккуратным, так как она предназначена для
удаления неотслеживаемых файлов из рабочей папки. Даже если вы передумаете,
восстановить содержимое таких файлов, как правило, будет невозможно. Безопаснее
воспользоваться командой git stash --all, удаляющей из папки все содержимое,
но с последующим его сохранением в буфере.
Предположим, вы все-таки хотите удалить командой git clean мусорные файлы
или очистить вашу рабочую папку. Для удаления из этой папки всех неотслеживаемых файлов используйте команду git clean -f -d, которая полностью очищает
папку, убирая не только файлы, но и вложенные папки. Параметр -f (сокращение
от слова force — заставлять) означает принудительное удаление, подчеркивая, что
вы действительно хотите это сделать.
Если же вам интересно посмотреть на результаты удаления, используйте параметр –n, который говорит системе: «Сымитируй удаление и покажи мне, что будет
удалено».

238    Глава 7 • Git-инструментарий
$ git clean -d -n
Would remove test.o
Would remove tmp/

По умолчанию команда git clean удаляет только неотслеживаемые файлы, не
добавленные в список игнорируемых. Любой файл, имя которого совпадает с шаблоном в файле .gitignore, сохранится. Чтобы удалить и их, например убрав все
генерируемые в процессе сборки файлы с расширением .o с целью полной очистки
сборки, добавьте к команде clean параметр -x.
$ git status -s
M lib/simplegit.rb
?? build.TMP
?? tmp/
$ git clean -n -d
Would remove build.TMP
Would remove tmp/
$ git
Would
Would
Would

clean -n -d -x
remove build.TMP
remove test.o
remove tmp/

Если вы точно не знаете, к каким последствиям приведет выполнение команды git clean, всегда добавляйте к ней параметр -n, чтобы посмотреть на результат перед тем, как указать параметр -f и проводить реальное удаление. Другой
способ контроля над процессом дает параметр –i, включающий интерактивный
режим.
Вот пример выполнения команды очистки в интерактивном режиме.
$ git clean -x -i
Would remove the following items:
build.TMP test.o
*** Commands ***
1: clean 2: filter by pattern 3: select by numbers 4: ask each 5: quit
6: help
What now>

Это дает вам возможность по очереди рассмотреть все удаляемые файлы или в интерактивном режиме указать шаблон удаления.

Подпись
Благодаря шифрованию система Git является безопасной, но полностью она не
защищена. Для проверки того факта, что пересылаемые через Интернет файлы
исходят из доверенного источника, в Git существует несколько вариантов подписи
и проверки исходного кода при помощи программы GPG.

Подпись   239

Знакомство с GPG
Чтобы иметь возможность что-то подписывать, вам прежде всего нужен настроенный в программе GPG и установленный личный ключ.
$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
--------------------------------pub
2048R/0A46826A 2014-06-04
uid
Scott Chacon (Git signing key)
sub
2048R/874529A9 2014-06-04

Если ключа у вас пока нет, сгенерируйте его командой gpg --gen-key.
gpg --gen-key

Чтобы заставить Git использовать для подписи ваш закрытый ключ, установите
значение конфигурационного параметра user.signingkey:
git config --global user.signingkey 0A46826A

Теперь Git будет по умолчанию использовать этот ключ для подписи тегов и коммитов.

Подпись тегов
После настройки закрытого GPG-ключа его можно использовать для подписи
новых тегов. Достаточно заменить параметр -a параметром -s:
$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: „Ben Straub "
2048-bit RSA key, ID 800430EB, created 2014-05-04

Если запустить для этого тега команду git show, вы увидите присоединенную к тегу
GPG-подпись:
$ git show v1.5
tag v1.5
Tagger: Ben Straub
Date:
Sat May 3 20:29:41 2014 -0700
my signed 1.5 tag
-----BEGIN PGP SIGNATURE----Version: GnuPG v1
iQEcBAABAgAGBQJTZbQlAAoJEF0+sviABDDrZbQH/09PfE51KPVPlanr6q1v4/Ut
LQxfojUWiLQdg2ESJItkcuweYg+kc3HCyFejeDIBw9dpXt00rY26p05qrpnG+85b
hM1/PswpPLuBSr+oCIDj5GMC2r2iEKsfv2fJbNW8iWAXVLoWZRF8B0MfqX/YTMbm
ecorc4iXzQu7tupRihslbNkfvfciMnSDeSvzCpWAHl7h8Wj6hhqePmLm9lAYqnKp
8S5B/1SSQuEAjRZgI4IexpZoeKGVDptPHxLLS38fozsyi0QyDyzEgJxcJQVMXxVi
RUysgqjcpT8+iQM1PblGfHR4XAhuOqN5Fx06PSaFZhqvWFezJ28/CLyX5q+oIVk=
=EFTF
-----END PGP SIGNATURE-----

240    Глава 7 • Git-инструментарий
commit ca82a6dff817ec66f44342007202690a93763949
Author: Scott Chacon
Date: Mon Mar 17 21:52:11 2008 -0700
changed the verison number

Проверка тегов
Проверка подписанного тега выполняется командой git tag -v [имя-тега]. Эта
команда использует для проверки GPG. Для ее корректной работы нужно, чтобы
в вашем хранилище ключей присутствовал открытый ключ автора, поставившего
подпись:
$ git tag -v v1.4.2.1
object 883653babd8ee7ea23e6a5c392bb739348b1eb61
type commit
tag v1.4.2.1
tagger Junio C Hamano 1158138501 -0700
GIT 1.4.2.1
Minor fixes since 1.4.2, including git-mv and git-http with alternates.
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Good signature from "Junio C Hamano "
gpg:
aka "[jpeg image of size 1513]"
Primary key fingerprint: 3565 2A26 2040 E066 C9A7 4A7D C0C6 D9A4 F311 9B9A

Если открытый ключ автора у вас отсутствует, вы увидите нечто подобное:
gpg: Signature made Wed Sep 13 02:08:25 2006 PDT using DSA key ID F3119B9A
gpg: Can't check signature: public key not found
error: could not verify the tag 'v1.4.2.1'

Подпись коммитов
В новых версиях Git (от v1.7.9 и выше) появилась возможность подписывать отдельные коммиты. Если вы предпочитаете подписывать не теги, а сами коммиты,
добавьте к команде git commit параметр -S.
$ git commit -a -S -m 'signed commit'
You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) "
2048-bit RSA key, ID 0A46826A, created 2014-06-04
[master 5c3386c] signed commit
4 files changed, 4 insertions(+), 24 deletions(-)
rewrite Rakefile (100%)
create mode 100644 lib/git.rb

Для просмотра и проверки таких подписей команда git log снабжена параметром
--show-signature:

Подпись   241
$ git log --show-signature -1
commit 5c3386cf54bba0a33a32da706aa52bc0155503c2
gpg: Signature made Wed Jun 4 19:49:17 2014 PDT using RSA key ID 0A46826A
gpg: Good signature from "Scott Chacon (Git signing key) "
Author: Scott Chacon
Date:
Wed Jun 4 19:49:17 2014 -0700
signed commit

Кроме того, существует параметр форматирования %G?, заставляющий команду git
log проверять все обнаруженные подписи и выводить их в виде списка:
$ git log --pretty="format:%h %G? %aN %s"
5c3386c
ca82a6d
085bb3b
a11bef0

G
N
N
N

Scott
Scott
Scott
Scott

Chacon
Chacon
Chacon
Chacon

signed commit
changed the verison number
removed unnecessary test code
first commit

Здесь мы видим, что подписан и действителен только последний коммит, а все
остальные — нет.
Начиная с версии 1.8.3, команды git merge и git pull с помощью параметра
--verify-signatures можно заставить проверять слияния и отклонять их, если
коммит не содержит доверенной GPG-подписи.
Если вы воспользуетесь этим параметром при слиянии с веткой, содержащей неподписанные и недействительные коммиты, слияние выполнено не будет:
$ git merge --verify-signatures non-verify
fatal: Commit ab06180 does not have a GPG signature.

Если же ветка, с которой осуществляется слияние, содержит только корректно подписанные коммиты, команда merge сначала покажет все проверенные ею подписи,
а потом перейдет непосредственно к слиянию:
$ git merge --verify-signatures signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key)

Updating 5c3386c..13ad65e
Fast-forward
README | 2 ++
1 file changed, 2 insertions(+)

Можно также воспользоваться параметром -S команды git merge для подписи
коммита, образующегося в результате слияния. В следующем примере выполняется
проверка наличия подписи у каждого предназначенного для слияния коммита,
а затем итоговый коммит слияния получает подпись:
$ git merge --verify-signatures -S signed-branch
Commit 13ad65e has a good GPG signature by Scott Chacon (Git signing key)

You need a passphrase to unlock the secret key for
user: "Scott Chacon (Git signing key) "

242    Глава 7 • Git-инструментарий
2048-bit RSA key, ID 0A46826A, created 2014-06-04
Merge made by the 'recursive' strategy.
README | 2 ++
1 file changed, 2 insertions(+)

Подпись должна быть у всех
Подписывать теги и коммиты важно и нужно, но если вы решите ввести эту операцию в обычный рабочий процесс, следует убедиться, что делать это умеют все
члены группы. В противном случае придется потратить уйму времени, объясняя
коллегам, как перезаписать их коммиты в подписанном виде. Перед тем как внедрить процедуру подписи в рабочий процесс, убедитесь, что вы хорошо понимаете,
что такое GPG и какие преимущества дает эта программа.

Поиск
Практически при любом размере базы кода рано или поздно возникает задача поиска места, в котором вызывается или определяется функция, или поиска истории
метода. Для поиска в коде и коммитах, хранящихся в базе данных Git, есть ряд полезных инструментов. Некоторые из них мы рассмотрим в этом разделе.

Команда git grep
В Git есть команда git grep, позволяющая искать строки и регулярные выражения в дереве коммитов или в рабочей папке. Для иллюстрации ее применения мы
возьмем код самой системы Git.
По умолчанию команда git grep выполняет поиск среди файлов вашей рабочей
папки. Параметр -n указывает максимальное количество обнаруженных совпадений,
которые будут фигурировать в выводимых командой данных:
$ git grep -n gmtime_r
compat/gmtime.c:3:#undef gmtime_r
compat/gmtime.c:8:
return git_gmtime_r(timep, &result);
compat/gmtime.c:11:struct tm *git_gmtime_r(const time_t *timep, struct tm *result)
compat/gmtime.c:16:
ret = gmtime_r(timep, result);
compat/mingw.c:606:struct tm *gmtime_r(const time_t *timep, struct tm *result)
compat/mingw.h:162:struct tm *gmtime_r(const time_t *timep, struct tm *result);
date.c:429:
if (gmtime_r(&now, &now_tm))
date.c:492:
if (gmtime_r(&time, tm)) {
git-compat-util.h:721:struct tm *git_gmtime_r(const time_t *, struct tm *);
git-compat-util.h:723:#define gmtime_r git_gmtime_r

У команды grep есть еще ряд интересных параметров.

Поиск   243

Например, можно заставить Git обобщить выводимые командой данные, показав
только файлы, в которых обнаружены совпадения, вместе с количеством этих совпадений. Для этого вам потребуется параметр --count:
$ git grep --count gmtime_r
compat/gmtime.c:4
compat/mingw.c:1
compat/mingw.h:1
date.c:2
git-compat-util.h:2

Чтобы посмотреть, в каком методе или функции было обнаружено совпадение,
используйте параметр -p:
$ git grep -p gmtime_r *.c
date.c=static int match_multi_number(unsigned long num, char c, const char *date,
char *end, struct
tm *tm)
date.c:
if (gmtime_r(&now, &now_tm))
date.c=static int match_digit(const char *date, struct tm *tm, int *offset, int
*tm_gmt)
date.c:
if (gmtime_r(&time, tm)) {

Вывод этой команды показывает, что метод gmtime_r вызывается функциями
match_multi_number и match_digit, находящимися в файле date.c.
Флаг --and позволяет искать сложные комбинации строк. Он гарантирует наличие
в одной строке нескольких совпадений. Давайте, к примеру, найдем в базе Git-кода
более старой версии 1.8.0 строки, содержащие определение константы, посредством
строковых переменных LINK или BUF_MAX.
Заодно мы воспользуемся параметрами --break и --heading, чтобы придать выводимым данным более читабельный вид:
$ git grep --break --heading \
-n -e '#define' --and \( -e LINK -e BUF_MAX \) v1.8.0
v1.8.0:builtin/index-pack.c
62:#define FLAG_LINK (1u cumulative).

Итак, импортированный репозиторий готов к отправке на новый сервер.

410    Глава 9 • Git и другие системы контроля версий

TFS
Если ваша рабочая группа решила передать процесс управления версиями от TFVC
к Git, процесс перехода должен выполняться с максимально доступной точностью.
Поэтому хотя ранее мы познакомились с двумя инструментами — git-tfs и git-tf,
сейчас речь пойдет только о git-tfs, так как именно он поддерживает ветки. Кроме
того, импорт средствами git-tf представляет собой чрезмерно сложную процедуру.
ПРИМЕЧАНИЕ
В данном случае речь идет о необратимом преобразовании. Полученный в итоге Gitрепозиторий подключить к исходному TFVC-проекту вы уже не сможете.

Первым делом следует выполнить проецирование имен пользователей. Система TFVC достаточно вольно подходит к данным, сохраняемым для наборов
изменений в поле author, в то время как Git требует удобочитаемого имени и
адреса электронной почты. Получить эту информацию можно с помощью клиента
­командной строки tf:
PS> tf history $/myproject -recursive | cut -b 11-20 | tail -n+3 | uniq | sort >
AUTHORS

В данном случае мы рассматриваем все наборы изменений в истории проекта.
Команда cut игнорирует все, кроме символов 11–20 из каждой строки (в данном
случае для получения корректных цифр вам придется поэкспериментировать с
длиной строк). Команда tail пропускает первые две строки, содержащие заголовки
полей и подчеркивания, выполненные ASCII-графикой. Затем все данные передаются команде uniq, которая убирает дубликаты, а конечный результат сохраняется
в файле AUTHORS. Дальше приходится действовать вручную; инструмент git-tfs
может результативно пользоваться этим файлом только в случае, когда каждая
строка имеет вот такой формат:
DOMAIN\username = User Name

Слева от знака равенства мы видим содержимое поля User в системе TFVC, в то время как справа фигурирует имя пользователя, которое применяется в Git-коммитах.
Теперь нужно выполнить клонирование интересующего нас TFVC-проекта:
PS> git tfs clone --with-branches --authors=AUTHORS https://username.visualstudio.com/
DefaultCollection $/project/Trunk project_git

Затем вы, возможно, захотите почистить разделы git-tfs-id в нижней части сообщений фиксации. Это делается следующей командой:
PS> git filter-branch -f --msg-filter 'sed "s/^git-tfs-id:.*$//g"' -- --all

В данном случае команда sed для среды Git-bash заменяет все строки, начинающиеся с префикса git-tfs-id:, пустой строкой, которую система Git впоследствии
успешно игнорирует.

Переход на Git   411

После этого остается только добавить новый удаленный репозиторий, отправить
туда все ветки и приступить к работе с Git.

Другие варианты импорта
Если вы пользуетесь системой контроля версий, не входящей в число рассмотренных
ранее, можно поискать средство импорта в Интернете. Существуют качественные
программы для множества систем, в том числе CVS, Clear Case, Visual Source Safe
и даже обычной папки с архивами. Если же ни одна из готовых программ вам не
подойдет, потому что вы работаете с малоизвестной системой контроля версий
или вам требуется больший контроль над процедурой импорта, остается команда
git fast-import. Она читает простые инструкции из стандартного потока ввода и
записывает определенные данные в Git. Это намного более простой способ созда­
ния Git-объектов, чем запуск низкоуровневых Git-команд или попытка записи
низкоуровневых объектов (более подробно эта тема рассматривается в главе 10).
В данном же случае можно написать сценарий импорта, читающий из исходной
системы нужную информацию и отправляющий четкие инструкции в стандартный
поток вывода. После чего остается запустить этот сценарий и передать результат
его работы непосредственно команде git fast-import.
Рассмотрим написание простой программы импорта. Предполагается, что мы работаем в папке current и периодически делаем резервные копии проекта в папках,
имена которых строятся по принципу back_ГГГГ_ММ_ДД.
Итак, у нас возникла необходимость импортировать эти данные в Git. Вот структура наших папок:
$ ls /opt/import_from
back_2014_01_02
back_2014_01_04
back_2014_01_14
back_2014_02_03
current

Чтобы импортировать папку Git, следует освежить в памяти способ хранения данных в Git. Как вы, возможно, помните, система Git, по сути, представляет собой
связанный список объектов-коммитов, каждый из которых указывает на снимок
состояния данных. Поэтому нам нужно всего лишь указать команде fast-import
снимки состояния, входящие в них данные коммитов и порядок их следования.
Мы по очереди рассмотрим все снимки и создадим коммиты с содержимым каждой
папки, связывая каждый следующий коммит с предыдущим.
Как и в главе 8, мы напишем сценарий на языке Ruby, потому что обычно пишем именно на этом языке, кроме того, он прост для понимания. Вы же можете воспользоваться своим любимым языком программирования, ведь нам
требу­ется только включить нужную информацию в стандартный поток вывода.

412    Глава 9 • Git и другие системы контроля версий
Кроме того, тем, кто работает в операционной системе Windows, следует особо
позабо­титься о том, чтобы в конце строк не появлялись символы возврата каретки.
Дело в том, что команда git fast-import работает только с символами перевода
строки (LF).
Первым делом зайдем в исходную папку и определимся с подпапками, содержащими снимки состояния, которые мы хотим импортировать как коммиты. Мы будем
по очереди заходить в каждую такую подпапку и запускать команды экспорта.
Вот главный базовый цикл:
last_mark = nil
# по очереди просматриваем папки
Dir.chdir(ARGV[0]) do
Dir.glob("*").each do |dir|
next if File.file?(dir)
# переход в целевую папку
Dir.chdir(dir) do
last_mark = print_export(dir, last_mark)
end
end
end

Внутри каждой папки запускается метод print_export, который берет манифест
и метку предыдущего снимка состояния и возвращает манифест и метку текущего;
именно это обеспечивает корректность их связывания. Термин «метка» в контексте
команды fast-import означает идентификатор, который вы присваиваете коммиту
в момент создания и который впоследствии используется для связи с другими коммитами. Соответственно первым действием метода print_export будет генерация
метки из имени папки:
mark = convert_dir_to_mark(dir)

Мы создадим массив папок, взяв в качестве метки значение индекса, так как в роли
метки может выступать только целое число. Наш метод мог бы выглядеть так:
$marks = []
def convert_dir_to_mark(dir)
if !$marks.include?(dir)
$marks 'log'
'--graph' '--pretty=oneline'
'--abbrev-commit' '--decorate' '--all'
trace: built-in: git 'log' '--graph'
'--pretty=oneline' '--abbrev-commit'
'--decorate' '--all'
trace: run_command: 'less'
trace: exec: 'less'

Переменная GIT_TRACE_PACK_ACCESS регистрирует доступ к pack-файлам. Первое
поле содержит pack-файл, к которому вы обращаетесь, а второе — смещение внутри
этого файла:
$ GIT_TRACE_PACK_ACCESS=true git status
20:10:12.081397 sha1_file.c:2088 .git/objects/pack/pack-c3fa...291e.pack
20:10:12.081886 sha1_file.c:2088 .git/objects/pack/pack-c3fa...291e.pack
20:10:12.082115 sha1_file.c:2088 .git/objects/pack/pack-c3fa...291e.pack
# [...]
20:10:12.087398 sha1_file.c:2088 .git/objects/pack/pack-e80e...e3d2.pack
20:10:12.087419 sha1_file.c:2088 .git/objects/pack/pack-e80e...e3d2.pack
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

12
34662
35175
56914983
14303666

Переменная GIT_TRACE_PACKET активирует регистрацию сетевых операций на
уровне пакетов:
$ GIT_TRACE_PACKET=true git ls-remote origin
20:15:14.867043 pkt-line.c:46 packet: git< # service=git-upload-pack
20:15:14.867071 pkt-line.c:46 packet: git< 0000
20:15:14.867079 pkt-line.c:46 packet: git<
97b8860c071898d9e162678ea1035a8ced2f8b1f HEAD\0multi_ack thin-pack side-band sideband-64k ofsdelta
shallow no-progress include-tag multi_ack_detailed no-done symref=HEAD:refs/heads/
master
agent=git/2.0.4
20:15:14.867088 pkt-line.c:46 packet: git<
0f20ae29889d61f2e93ae00fd34f1cdb53285702 refs/heads/ab/add-interactive-show-difffunc-name
20:15:14.867094 pkt-line.c:46 packet: git<
36dc827bc9d17f80ed4f326de21247a5d1341fbc refs/heads/ah/doc-gitk-config
# [...]

Переменная GIT_TRACE_PERFORMANCE отвечает за регистрацию сведений о производительности. Выводимые данные показывают, сколько времени занимали те или
иные действия:
$ GIT_TRACE_PERFORMANCE=true git gc
20:18:19.499676 trace.c:414 performance: 0.374835000 s: git command: 'git'
'pack-refs' '--all' '--prune'
20:18:19.845585 trace.c:414 performance: 0.343020000 s: git command: 'git'
'reflog' 'expire' '--all'
Counting objects: 170994, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (43413/43413), done.

456    Глава 10 • Git изнутри
Writing objects: 100% (170994/170994), done.
Total 170994 (delta 126176), reused 170524 (delta 125706)
20:18:23.567927 trace.c:414 performance: 3.715349000 s: git command: 'git'
'packobjects' '--keep-true-parents'
'--honor-pack-keep' '--non-empty' '--all'
'--reflog' '--unpackunreachable= 2.weeks.ago'
'--local' '--delta-base-offset'
'.git/objects/pack/.tmp-49190-pack'
20:18:23.584728 trace.c:414 performance: 0.000910000 s: git command: 'git'
'prune-packed'
20:18:23.605218 trace.c:414 performance: 0.017972000 s: git command: 'git'
'updateserver-info'
20:18:23.606342 trace.c:414 performance: 3.756312000 s: git command: 'git'
'repack' '-d' '-l' '-A'
'--unpack-unreachable=2.weeks.ago'
Checking connectivity: 170994, done.
20:18:25.225424 trace.c:414 performance: 1.616423000 s: git command: 'git'
'prune' '--expire' '2.weeks.ago'
20:18:25.232403 trace.c:414 performance: 0.001051000 s: git command: 'git'
'rerere' 'gc'
20:18:25.233159 trace.c:414 performance: 6.112217000 s: git command: 'git'
'gc'

Переменная GIT_TRACE_SETUP показывает информацию, собранную системой Git
о репозитории и среде, с которой она взаимодействует:
$ GIT_TRACE_SETUP=true git status
20:19:47.086765 trace.c:315 setup: git_dir: .git
20:19:47.087184 trace.c:316 setup: worktree: /Users/ben/src/git
20:19:47.087191 trace.c:317 setup: cwd: /Users/ben/src/git
20:19:47.087194 trace.c:318 setup: prefix: (null)
On branch master
Your branch is up-to-date with 'origin/master'.
nothing to commit, working directory clean

Разное
Если переменная GIT_SSH задана, указанная в ней программа будет использоваться
вместо ssh при попытках системы Git подключиться к SSH-хосту. Порядок вызова
в данном случае следующий:
$GIT_SSH [имя пользователя@]хост [-p ]

Это не самый простой способ настроить вызовы ssh; дополнительные параметры
командной строки не поддерживаются, и вам, скорее всего, придется писать сценарий-надстройку, превращая переменную GIT_SSH в ссылку на этот сценарий.
Вероятно, проще воспользоваться файлом ~/.ssh/config.
Переменная GIT_ASKPASS переопределяет значение конфигурационного параметра
core.askpass. Это программа вызывается каждый раз, когда системе Git нужно
узнать учетные данные пользователя. Она ожидает текстового приглашения как

Заключение   457

аргумента командной строки и возвращает ответ в стандартный поток вывода.
(Более подробно см. раздел «Хранение учетных данных» в главе 7.)
Переменная GIT_NAMESPACE контролирует доступ к ссылкам внутри пространства
имен и эквивалентна флагу --namespace. В основном она используется на сто­роне
сервера, когда нужно сохранить в одном репозитории несколько версий этого репозитория, разделяя лишь ссылки.
Переменная GIT_FLUSH заставляет систему Git при инкрементной записи в стандартный поток вывода использовать небуферизованный ввод-вывод. Значение 1
увеличивает частоту сброса данных, а значение 0 делает вывод целиком буферизованным. Значение, предлагаемое по умолчанию (если эта переменная не задана),
выбирается в зависимости от выполняемых действий и режима вывода данных.
Переменная GIT_REFLOG_ACTION позволяет задать описание в журнале ссылок. Вот
пример:
$ GIT_REFLOG_ACTION="my action" git commit --allow-empty -m 'my message'
[master 9e3d55a] my message
$ git reflog -1
9e3d55a HEAD@{0}: my action: my message

Заключение
Теперь вы должны хорошо понимать, как Git функционирует в фоновом режиме,
и до некоторой степени разбираться в том, как система реализована. В этой главе
были рассмотрены некоторые служебные команды, более низкоуровневые и простые, чем команды, которыми вы пользовались в остальной части книги. Знание
того, как Git функционирует на низком уровне, облегчит вам понимание действий
этой системы и даст возможность писать собственные инструменты и вспомогательные сценарии для организации нестандартного рабочего процесса.
Будучи контентно-адресуемой файловой системой, Git является мощным инструментом, который может применяться не только как VCS. Надеемся, что сведения
о внутреннем устройстве Git помогут вам в реализации собственных вариантов
применения этой системы и дадут ощущение свободы даже при решении сложных
задач.

Приложение A
Git в других средах

Если вы прочитали книгу целиком, значит, вы уже много знаете о том, как пользоваться системой Git из командной строки. Вы умеете работать с локальными
файлами, синхронизировать репозитории по сети и эффективно работать с другими пользователями. Но это еще не все; Git обычно применяется как часть большой экосистемы, и терминал — далеко не лучший инструмент для работы с ней.
Рассмотрим несколько вариантов окружения, в которых вам может пригодиться
Git. Также вы узнаете, как другие приложения (в том числе и ваши собственные)
работают с этой системой.

Графические интерфейсы
Терминал является родной средой для Git. Именно там первым делом рождаются
новые функциональные возможности, и только из командной строки можно в полной мере ощутить всю мощь Git. Но текстовый интерфейс подходит для решения
далеко не всех задач; иногда более предпочтительным является графическое представление, а некоторым пользователям намного удобнее работать мышью.
Важно понимать, что каждый интерфейс оптимизирован под свои рабочие схемы.
Некоторые клиенты ограничены только теми функциональными возможностями, которые их автор считает наиболее востребованными или эффективными.
С этой точки зрения ни один из инструментов, о которых мы будем говорить
в этом приложении, не может быть «лучше» остальных; каждый из них просто

Графические интерфейсы   459

предназначен для решения конкретных задач. Кроме того, помните, что все задачи,
решаемые с помощью графического интерфейса, могут решаться и из командной
строки. И именно командная строка дает вам максимальный контроль над репозиториями.

Утилиты gitk и git-gui
После установки Git вы получаете два графических инструмента: gitk и git-gui.
Инструмент gitk предназначен для просмотра истории. Представьте, что у вас есть
мощная GUI-оболочка для команд git log и git grep. Именно этим инструментом
вы будете пользоваться для поиска прошедших событий и визуализации истории
проекта.
Проще всего gitk вызывается из командной строки. Перейдите в Git-репозиторий
и введите:
$ gitk [git log параметры]

Инструмент gitk принимает множество различных параметров командной строки,
большинство из которых передается базовой команде git log. Одним из наиболее
применяемых является параметр --all, заставляющий инструмент gitk показывать коммиты, достижимые по любой ссылке, а не только те, на которые нацелен
указатель HEAD (рис. А.1).
Вверху находится рисунок, напоминающий вывод команды git log --graph; каждая
точка соответствует коммиту, линии показывают соотношения между коммитами,
а ссылки представлены цветными прямоугольниками. Желтая точка означает
указатель HEAD, а красная — изменения, которые попадут в следующий коммит.
В нижней части окна дано представление выделенного коммита; комментарии
и исправления — слева, а обобщенный вид — справа. В центре расположен набор
элементов управления для поиска в истории проекта.
Второй инструмент, git-gui, в основном предназначен для редактирования коммитов (рис. А.2). Он также легко вызывается из командной строки:
$ git gui

Слева находится область индексирования; неиндексированные изменения располагаются сверху, а индексированные — снизу. Файлы можно перемещать из одного
состояния в другое, щелкая на их значках. Чтобы выбрать файл для просмотра,
просто щелкните на его имени.
Справа вверху находится область просмотра изменений в выделенном файле. Можно
индексировать отдельные фрагменты кода (или отдельные строки), щелкая на этой
области правой кнопкой мыши.

460    Приложение А • Git в других средах

Рис. A.1. gitk — инструмент для просмотра истории

Рис. A.2. git-gui — инструмент для работы с коммитами

Графические интерфейсы   461

Справа снизу мы видим область для ввода сообщений фиксации и несколько
кнопок. Введите в текстовое поле сообщение и щелкните на кнопке Commit, чтобы
выполнить действие, напоминающее команду git commit. Кроме того, в последний
коммит можно вносить правки, установив переключатель Amend Last Commit. Это
обновляет область Staged Changes, добавляя туда содержимое последнего коммита.
После этого вы можете индексировать изменения и убирать их из индекса, редактировать сообщение фиксации, а затем щелчком на кнопке Commit заменить старый
коммит новым.
Инструменты gitk и git-gui являются примерами приложений, ориентированных
на решение конкретных задач. У каждого из них есть определенное предназначение
(просмотр истории и создание коммитов соответственно), поэтому функциональные возможности, которые не требуются для решения поставленных задач, там
попросту не поддерживаются.

GitHub-клиенты для Mac и Windows
Компания GitHub создала два Git-клиента, ориентированных на рабочий процесс:
для Windows и для Mac (рис. А.3 и А.4). Эти клиенты демонстрируют замечательный
пример ориентированности на решение конкретных задач. Они предоставляют доступ не ко всем функциональным возможностям системы Git, а только к наиболее
востребованным, хорошо работающим друг с другом.

Рис. A.3. GitHub-клиент для Mac

Они спроектированы по одному шаблону, поэтому мы будем рассматривать
их как один продукт. В детали мы углубляться не будем (в конце концов,

462    Приложение А • Git в других средах
у этих инструментов существует документация), а в основном поговорим о представлении изме­нений (именно за этим занятием вы будете проводить большую
часть времени).
Слева находится список отслеживаемых репозиториев; для добавления репозитория
(путем клонирования или путем присоединения локальной копии) щелкните на
кнопке + в верхней части этой области.

Рис. A.4. GitHub-клиент для Windows

В центре находится область ввода коммита. Здесь вы указываете сообщение
фиксации и файлы, которые следует включить в коммит. (В версии для Windows
история коммитов демонстрируется сразу под этой областью, а в версии для Mac
она находится на отдельной вкладке.)
Справа располагается область просмотра изменений, показывающая, что изменилось в рабочей папке и какие изменения были добавлены в выбранный коммит.
Следует также обратить внимание на кнопку Sync в верхней правой части. Именно
она предлагает основной способ взаимодействия по сети.
ПРИМЕЧАНИЕ
Для работы с этими инструментами учетная запись на сайте GitHub не требуется. Хотя
они и спроектированы под GitHub-службу, их можно использовать с любым репозиторием и любым Git-сервером.

Графические интерфейсы   463

Установка
Интерфейс GitHub для Windows можно скачать со страницы https://windows.github.
com, а GitHub для Mac находится по адресу https://mac.github.com. При первом запуске
оба приложения выполнят первоначальную настройку Git, например укажут ваши
имя и адрес электронной почты, а также зададут разумные значения, предлагаемые
по умолчанию для многих распространенных конфигурационных параметров, таких
как кэши учетных данных и обработка символов CRLF.
Оба инструмента поддерживают фоновое автоматическое скачивание и установку
обновлений. Это относится и к связанной с ними версии системы Git, так что вам,
возможно, никогда не придется обновлять ее вручную. В операционной системе
Windows к клиенту прилагается также ярлык для запуска оболочки Powershell
с пакетом Posh-Git, которые мы рассмотрим чуть позже.
Теперь нужно предоставить инструменту репозитории, с которыми он будет работать. Клиент показывает список репозиториев, доступных на сайте GitHub, любой
из которых допускает клонирование одним щелчком. Если локальный репозиторий
у вас уже есть, просто перетащите его папку из приложения Finder или Windows
Explorer в окно GitHub-клиента, и оно окажется в расположенном слева списке
репозиториев.

Рекомендуемый рабочий процесс
Установленный и настроенный GitHub-клиент позволяет решать многие типовые
для Git задачи. Оптимальную рабочую схему для этого инструмента мы подробно
рассмотрели в разделе «Схема работы с GitHub» главы 6, но вкратце она выглядит
как регулярная фиксация состояний ветки и синхронизация с удаленным репозиторием.
Управление ветками относится к одной из немногих вещей, которая в этих инструментах реализована по-разному. В версии для Mac в верхней части окна находится
кнопка создания новой ветки (рис. А.5).

Рис. A.5. Кнопка создания ветки в версии для Mac

В версии для Windows нужно ввести имя новой ветки в виджет, управляющий
сменой веток (рис. А.6).
Добавление коммитов в готовую ветку осуществляется тривиально. Внесите изменения в рабочую папку, и при переходе в окно GitHub-клиента вы увидите, какие
файлы были модифицированы. Введите сообщение фиксации, выберите файлы,
которые вы планируете включить в коммит, и щелкните на кнопке Commit (либо
нажмите клавиатурную комбинацию Ctrl+Enter или +Enter).

464    Приложение А • Git в других средах

Рис. A.6. Создание ветки в версии для Windows

Основное средство взаимодействия с другими репозиториями — кнопка Sync.
В Git существуют отдельные команды для отправки данных на сервер, скачивания с
сервера информации, слияния и перемещения, а вот в GitHub-клиенте все это реализует один программный компонент. Вот что происходит после щелчка на кнопке Sync:
1. git pull --rebase. Если по причине конфликта слияния эта команда дает
сбой, происходит возвращение в предшествующее состояние командой git
pull --norebase.
2. git push.
Это наиболее распространенная последовательность команд при работе в данном
стиле, поэтому их совмещение экономит массу времени.

Подводя итоги
Рассмотренные инструменты отлично справляются с задачами, для решения которых они были спроектированы. Разработчики (и не только они) могут с их помощью
в считанные минуты начать работу над совместным проектом, причем многие распространенные операции реализованы в виде встроенных элементов интерфейса.
Впрочем, если вы предпочитаете другой рабочий процесс или ожидаете большего
контроля над выполнением сетевых операций, рекомендуем обратиться к другому
клиенту или к командной строке.

Другие GUI
Существует множество графических клиентов для работы с Git, начиная со специализированных, предназначенных для решения конкретных задач, и заканчивая
инструментами, пытающимися воплотить всю доступную в Git функциональность.
На официальном сайте Git вы найдете актуальный список наиболее популярных
клиентов. Он располагается на странице http://git-scm.com/downloads/guis. Еще более

Git в Visual Studio   465

подробный список доступен в Git-вики по адресу https://git.wiki.kernel.org/index.php/
Interfaces,_frontends,_and_tools#Graphical_Interfaces.

Git в Visual Studio
Начиная с версии Visual Studio 2013 обновление 1, пользователям стал доступен
встроенный в IDE Git-клиент. В Visual Studio уже довольно давно добавлена
функциональность управления исходным кодом, но она была ориентирована на
централизованные системы с блокировкой файлов, а система Git не очень вписывается в такой рабочий процесс. Поддержка Git в Visual Studio 2013 была отделена
от более старых функциональных особенностей, что позволило добиться лучшей
интеграции этих двух инструментов.
Откройте проект, который управляется системой Git (или выполните команду git
init для существующего проекта), и выберите в меню View команду Team Explorer.
Откроется окно Connect (рис. А.7).

Рис. A.7. Подключение к репозиторию Git через Team Explorer

Приложение Visual Studio запоминает все открываемые вами проекты, управляемые
системой Git, формируя их список в нижней части окна. Если вы не видите в списке
нужного проекта, щелкните на ссылке Add и укажите путь к рабочей папке. Двойной
щелчок на имени локального Git-репозитория откроет его главную страницу Home,
которая показана на рис. A.8. Это центр действий, связанных с Git; при написании
кода вы, как правило, проводите больше всего времени на странице Changes, а когда

466    Приложение А • Git в других средах
дело доходит до скачивания результатов труда ваших коллег, переходите на страницы Unsynced Commits и Branches.

Рис. A.8. Главная страница
Git-репозитория в Visual Studio

В Visual Studio в настоящее время имеется мощный, ориентированный на решение
конкретных задач пользовательский интерфейс для Git. В него входят средства линейного представления истории, просмотра добавленных изменений, выполнения
удаленных команд и многие другие. Познакомиться с их полным перечнем (рассмотрение которого выходит за рамки темы данной книги) вы можете на странице
http://msdn.microsoft.com/en-us/library/hh850437.aspx.

Git в Eclipse
Приложение Eclipse поставляется с подключаемым модулем Egit, предоставляющим достаточно полный интерфейс для работы с Git (рис. А.9). Для доступа к
нему следует перейти к Git-представлению (выбрав в меню Window команду Open
Perspective, а затем вариант Other и выбрав Git).

Git в Bash
Пользователи командной оболочки Bash могут задействовать некоторые ее функциональные возможности для упрощения работы с Git. Система Git поставляется
с подключаемыми модулями нескольких оболочек, но по умолчанию они не работают.
Первым делом скопируйте из репозитория с исходным Git-кодом файл contrib/
completion/git-completion.bash. Поместите копию в доступное место, например
в папку home, и добавьте в файл .bashrc такую строку:
. ~/git-completion.bash

Git в Bash   467

Рис. A.9. Среда EGit в Eclipse

Затем перейдите в Git-репозиторий и наберите следующую команду:
$ git chec

В результате оболочка Bash автоматически добавит в строку команду git checkout.
Этот прием работает для всех Git-команд, параметров командной строки, а где это
возможно еще и для имен удаленных серверов и ссылок.
Кроме того, имеет смысл настроить в приглашении на ввод режим отображения
информации о связанном с текущей папкой Git-репозитории. Можно вывести
сколь угодно сложную информацию, но обычно большинству пользователей хватает сведений о текущей ветке и состоянии рабочей папки. Для добавления этой
информации достаточно скопировать файл contrib/completion/git-prompt.sh из
репозитория с исходным Git-кодом в свою папку home и добавить в файл .bashrc,
к примеру, следующие строки:
. ~/git-prompt.sh
export GIT_PS1_SHOWDIRTYSTATE=1
export PS1='\w$(__git_ps1 " (%s)")\$ '

Запись \w означает показ текущей рабочей папки, символы \$ означают вывод
в приглашении на ввод символа $, а запись __git_ps1 " (%s)" вызывает вместе
с аргументом форматирования функцию, предоставляемую файлом git-prompt.sh.­

468    Приложение А • Git в других средах
После этого при вызове в рамках Git-проектов приглашение на ввод команды будет
выглядеть так, как показано на рис. А.10.
Оба этих сценария снабжены исчерпывающей документацией; дополнительную
информацию вы найдете в файлах git-completion.bash и git-prompt.sh.

Рис. A.10. Специальный вариант приглашения на ввод команды в bash

Git в Zsh
Кроме того, система Git поставляется с библиотекой автоматического заполнения
при нажатии клавиши табуляции в командной оболочке Zsh. Скопируйте файл
contrib/completion/git-completion.zsh в папку home и укажите путь к нему в
конфигурационном файле .zshrc. Командная оболочка Zsh обладает куда более
мощным интерфейсом, чем Bash:
$ git che
check-attr -- display gitattributes information
check-ref-format -- ensure that a reference name is well formed
checkout -- checkout branch or paths to working tree
checkout-index -- copy files from index to working directory
cherry -- find commits not merged upstream
cherry-pick -- apply changes introduced by some existing commits

Допускающие двоякое толкование варианты автоматического заполнения не
­просто перечислены, а снабжены исчерпывающими описаниями, и вы можете
­перемещаться по списку, снова и снова нажимая клавишу Tab. Этот режим работает не только для Git-команд, но и для их аргументов и даже имен объектов
(например, ссылок и удаленных серверов) внутри репозитория, а также для имен
файлов и прочих вещей, для которых в Zsh реализована функция автоматического
заполнения.
Настройка приглашения на ввод команды в Zsh происходит почти таким же способом, как и в Bash, но у вас есть возможность вывода информации справа. Чтобы
показывать имя ветки в этой дополнительной строке, добавьте в файл ~/.zshrc
следующие строки:
setopt prompt_subst
. ~/git-prompt.sh
export RPROMPT=$'$(__git_ps1 "%s")'

Git в Powershell   469

В результате при каждом переходе командной оболочки внутрь репозитория Git
с правой стороны будет появляться имя текущей ветки (рис. А.11).

Рис. A.11. Специальный вариант приглашения на ввод команды в zsh

Возможности настройки Zsh столько обширны, что существуют целые фреймворки, предназначенные для его улучшения. Один из таких проектов называется
oh-my-zsh и доступен по адресу https://github.com/robbyrussell/oh-my-zsh. Это система
подключаемых модулей с мощным набором правил автоматического заполнения
для Git и различными вариантами приглашений на ввод команды, многие из которых показывают информацию, связанную с контролем версий. Рисунок A.12
демонстрирует один из примеров настройки.

Рис. A.12. Пример темы из проекта oh-my-zsh

Git в Powershell
Стандартный терминал командной строки в Windows (cmd.exe) не предназначен для
работы с Git, но вы можете воспользоваться оболочкой Powershell. Пакет Posh-Git
(https://github.com/dahlbyk/posh-git) предоставляет мощные функции автоматического
заполнения и расширенные приглашения на ввод команд, помогая поддерживать
состояние репозитория на высоком уровне (рис. А.13).
Если у вас установлено GitHub-приложение для Windows, значит, есть и Posh-Git,
поэтому вам остается только добавить в файл profile.ps1 (который обычно находится в папке C:\Users\\Documents\WindowsPowerShell) следующие
строки:

470    Приложение А • Git в других средах
. (Resolve-Path "$env:LOCALAPPDATA\GitHub\shell.ps1")
. $env:github_posh_git\profile.example.ps1

Рис. A.13. Командная оболочка Powershell с Posh-Git

Остальным же нужно загрузить последнюю версию пакета Posh-Git (https://github.
com/dahlbyk/posh-git) и распаковать ее в папке WindowsPowershell. Затем запускается
Powershell с правами администратора и выполняются следующие команды:
> Set-ExecutionPolicy RemoteSigned -Scope CurrentUser -Confirm
> cd ~\Documents\WindowsPowerShell\posh-git
> .\install.ps1

Это добавит в файл profile.ps1 необходимые строки, и при следующем запуске
терминала вы получите доступ к Posh-Git.

Заключение
Теперь вы умеете пользоваться системой Git в рамках инструментов, применяемых
при решении каждодневных задач, и знаете, как получить доступ к репозиториям
Git из своих программ.

Приложение Б
Встраивание Git
в приложения

Если приложение, которое вы пишете, предназначено для разработчиков, скорее
всего, от интеграции с системой контроля версий оно только выиграет. Впрочем,
пользу из такой интеграции могут извлечь и приложения для обычных пользователей, например текстовые редакторы. Gitзамечательно работает в самых разных
сценариях.
Интегрировать Git в приложение можно тремя способами: вызвать командную
оболочку и воспользоваться Git-инструментами командной строки или библиотеками Libgit2 и JGit.

Командная строка
Итак, вы можете вызвать командную оболочку и воспользоваться для решения
текущих задач командной строкой. Это канонический подход, кроме того, вы получаете поддержку всех функциональных возможностей системы Git. Более того,
это очень простой способ, так как в большинстве сред выполнения предоставляется
относительно удобный доступ к вызову процессов с аргументами командной строки.
Но подход обладает и своими недостатками.

472    Приложение Б • Встраивание Git в приложения
Во-первых, выводимые командами данные имеют вид обычного текста. То есть вам
придется заставлять Git время от времени менять формат вывода для удобства
анализа информации, что далеко не всегда эффективно и повышает вероятность
ошибок.
Вторым недостатком является отсутствие способности восстанавливать работу
системы после ошибок. При повреждении репозитория или в случае некорректного
конфигурационного параметра Git просто отказывается выполнять большинство
операций.
Кроме того, необходимо управлять процессом. Система Git требует для среды оболочки отдельного процесса, что усложняет ситуацию. Координация набора таких
процессов (особенно если они работают с одним и тем же репозиторием) может
оказаться нетривиальной задачей.

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 \n", author->name, author->email);
const git_oid *tree_id = git_commit_tree_id(commit);
// Очистка
git_commit_free(commit);
git_object_free(head_commit);
git_repository_free(repo);

Первая пара строк открывает репозиторий Git. Тип git_repository является
дескриптором на репозиторий с кэшем в памяти. Это самый простой метод, которым можно пользоваться, если вы знаете точный путь к рабочей папке репозитория или к папке .git. Существует также вариант git_repository_open_ext,

Libgit2   473

принимающий параметры поиска, вариант git_clone и ему подобные для создания
клона удаленного репозитория, и вариант git_repository_init для создания
нового репозитория.
Во втором фрагменте кода фигурирует синтаксис rev-parse (подробно он рассматривался в разделе «Ссылки из веток» главы 7), помогающий получить коммит, на
который в данный момент нацелен указатель HEAD. Нам возвращают указатель git_
object, представляющий собой нечто, существующее в базе данных Git-объектов.
По существу, тип git_object является предком для ряда других объектов; во всех
случаях используется одна и та же структура памяти, поэтому можно безопасно
преобразовывать типы друг в друга. В данном случае git_object_type(commit)
вернет нам тип GIT_OBJ_COMMIT, который можно будет безопасно преобразовать
в указатель git_commit.
Следующий фрагмент кода показывает способ доступа к свойствам коммита. В последней строке фигурирует тип git_oid; он является внутренним представлением
контрольной суммы SHA-1 в библиотеке Libgit2.
В этом примере прослеживаются следующие закономерности.
‰‰ Если вы объявили указатель и передали ссылку в какую-либо Libgit2-функцию,

вам, скорее всего, вернут целочисленный код ошибки. Значение 0 указывает на
успешное выполнение операции; все, что меньше, означает ошибку.

‰‰ Если библиотека Libgit2 загружает для вас указатель, его освобождение стано-

вится вашей задачей.

‰‰ Если библиотека Libgit2 возвращает после вызова функции указатель const,

освобождать его не нужно. Он сам становится недействительным при уничтожении объекта, на который ссылается.

‰‰ Писать код на языке C не так-то просто.

Последний пункт означает, что вам вряд ли придется пользоваться языком C при
работе с библиотекой Libgit2. К счастью, существует ряд привязок для различных
языков, облегчающих работу с репозиториями Git из вашей среды исполнения.
Перепишем предыдущий пример с привязкой к языку Ruby для 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]} "
tree = commit.tree

Как видите, объем кода сократился. Во-первых, привязка Rugged использует исключения; она умеет сигнализировать о сбоях, вызывая ConfigError или ObjectError.
Во-вторых, вам не требуется в явном виде освобождать ресурсы, так как в языке
Ruby есть сборщик мусора. Рассмотрим более сложный пример — создание коммита «с нуля»:

474    Приложение Б • Встраивание Git в приложения
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. Для полей author и committer используется одна сигнатура.
5. Сообщение фиксации.
6. При создании коммитов требуется указывать их предков. В данном случае
родителем является указатель HEAD.
7. При создании коммита привязка Rugged (и библиотека Libgit2) может обновить ссылку.
8. Возвращаемое значение — это контрольная сумма SHA-1 нового коммита,
которую впоследствии можно использовать для доступа к этому объекту.
Код на языке Ruby получается чистым и лаконичным, так как основную работу выполняет библиотека Libgit2. Выполняется он также достаточно быстро. Варианты,
доступные тем, кто предпочитает писать код на других языках, мы рассмотрим
в разделе «Другие привязки».

Нетривиальная функциональность
Некоторые возможности библиотеки Libgit2 выходят за рамки системы Git. В частности, к ним относится расширяемость. Библиотека Libgit2 позволяет для части

Libgit2   475

операций использовать нестандартные «серверные приложения», сохраняя объекты не так, как это делает система Git. Подобные «приложения» существуют для
конфигурации, хранения ссылок и базы данных объектов.
Посмотрим, как это работает. Представленный здесь код заимствован из примеров,
написанных разработчиками библиотеки Libgit2 (вы найдете их на странице https://
github.com/libgit2/libgit2-backends). Вот настройка нестандартной базы данных объектов:
git_odb *odb;
int error = git_odb_new(&odb); (1)
git_repository *repo;
error = git_repository_wrap_odb(&repo, odb); (2)
git_odb_backend *my_backend;
error = git_odb_backend_mine(&my_backend, /*...*/); (3)
error = git_odb_add_backendodb, my_backend, 1); (4)

ПРИМЕЧАНИЕ
Ошибки в данном фрагменте перехватываются, но не обрабатываются. Надеемся, что
ваш код будет лучше нашего.

Целиком процедура выглядит так:
1. Инициализируется «интерфейс» пустой базы данных объектов (Object
DataBase, ODB), который послужит основой для реальной базы данных объектов.
2. Вокруг пустой базы данных объектов конструируется git_repository.
3. Инициализируется серверное приложение специализированной базы
данных объектов.
4. Репозиторий настраивается на использование этого приложения.
Но что такое 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));

476    Приложение Б • Встраивание Git в приложения
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 распределение памяти. Остальные поля — на ваше усмотрение; структура
может быть как большой, так и маленькой, в зависимости от того, что вам требуется.
Функция инициализации выделяет под эту структуру некий объем памяти, настраивает указанный вами контекст и заполняет поля поддерживаемой ею родительской
структуры. Полный набор сигнатур вызовов вы найдете в файле 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-вызовы подаются через API из удобной среды CLR. Наш пример выглядит
так:
new Repository(@"C:\path\to\repo").Head.Tip.Message;

Для приложений рабочего стола Windows существует даже пакет NuGet, позволяющий быстро приступить к работе.
Привязка objective-git
Если ваше приложение предназначено для платформы Apple, скорее всего, оно
написано на языке Objective-C. Для этой среды в Libgit2 используется привязка
Objective-Git (https://github.com/libgit2/objective-git). Вот пример программы:

Libgit2   477
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.resolve()
.get_object().message

# открытие репозитория
# получение прямой ссылки
# получение коммита, чтение сообщения

Дополнительная информация
К сожалению, детальное рассмотрение возможностей библиотеки Libgit2 выходит
за рамки темы данной книги. Чтобы лучше познакомиться с этой библиотекой,
начните с чтения документации к API, расположенной по адресу https://libgit2.github.
com/libgit2, а также с руководства на странице https://libgit2.github.com/docs. Сведения
о привязках к другим языкам вы найдете в соответствующих файлах README и
тестовых примерах; там часто встречаются небольшие учебные пособия и ссылки
на дополнительные материалы.

Приложение В
Git-команды

В этой книге мы познакомили вас с десятками различных Git-команд, приложив
немало усилий, чтобы рассказать о них по порядку, постепенно добавляя к повествованию новые команды. Но в результате информация об их смысле и предназначении оказалась рассеянной по всей книге.
В этом приложении вы найдете все упоминавшиеся в книге команды, сгруппированные по областям их применения. Вместе с кратким описанием каждой команды
вы найдете ссылки на приведенные в книге примеры их применения.

Настройка и конфигурирование
Существуют две распространенные команды, востребованные как сразу после
установки Git, так и в повседневной практике. Это команды config и help.
git config
Сотни операций в системе Git выполняются с параметрами, заданными по умолчанию. Но в большинстве случаев эти параметры можно заменить другими предустановленными значениями или вовсе воспользоваться собственным вариантом
значения. Это относится к большинству вариантов настройки Git, от вашего имени
до цветовой схемы терминала и используемого редактора. Есть несколько файлов,
из которых команда git config считывает значения, что дает возможность задавать
параметры как на глобальном уровне, так и на уровне конкретного репозитория.

Копирование и создание проектов   479

Командой git config мы пользовались почти во всех главах.
‰‰ В разделе «Первая настройка Git» главы 1 мы, еще не начав работать с Git, вос-

пользовались этой командой для задания своего имени, адреса электронной
почты и предпочитаемого редактора.

‰‰ В разделе «Псевдонимы в Git» главы 2 мы показали, каким образом эта команда

позволяет создавать краткие варианты команд с длинными списками параметров,
избавляя нас от необходимости каждый раз все это набирать на клавиатуре.

‰‰ В разделе «Перемещение данных» главы 3 мы заставили команду git pull по

умолчанию использовать параметр --rebase.

‰‰ В разделе «Хранение учетных данных» главы 7 мы настроили репозиторий, куда

по умолчанию помещали HTTP-пароли.

‰‰ И наконец, этой команде посвящен практически весь раздел «Конфигурирование

системы Git» главы 8.

git help
Команда git help служит для вывода на экран справочной информации по всем Gitкомандам. В этом приложении мы кратко описываем самые популярные команды,­
узнать же полный список всех их параметров и флагов поможет команда git help
.
Мы познакомили вас с командой git help в разделе «Получение справочной информации» главы 1, а еще воспользовались ею в разделе «Настройка сервера» главы 4
для получения сведений о настройке ограниченной оболочки.

Копирование и создание проектов
Существуют два способа получения Git-репозитория. Можно скопировать существующий репозиторий или создать новый в какой-нибудь папке.
git init
Для превращения папки в новый Git-репозиторий, в котором можно будет контролировать версии файлов, достаточно воспользоваться командой git init.
‰‰ В разделе «Удаленные ветки» главы 3 мы кратко рассказали, как поменять за-

данное по умолчанию имя ветки master.

‰‰ Мы воспользовались этой командой для создания пустого голого репозитория

для работы на сервере в разделе «Размещение на сервере голого репозитория»
главы 4.

‰‰ Наконец, мы рассмотрели подробности функционирования этой команды в раз-

деле «Канализация и фарфор» главы 10.

480    Приложение В • Git-команды
git clone
Команда git clone иногда выступает в роли оболочки для других команд. Она
создает новую папку, переходит в нее и запускает команду git init для создания
пустого Git-репозитория. Затем она добавляет удаленный репозиторий (git remote
add) по URL-адресу, который вы ей передали (по умолчанию он получает имя
origin), выполняет из этого репозитория команду git fetch, а затем выгружает
самый последний коммит в рабочую папку командой git checkout.
Команда git clone встречается в этой книге десятки раз, но мы перечислим только
самые интересные случаи ее применения.
‰‰ Первое знакомство и объяснение принципа действия этой команды вы найдете

в разделе «Клонирование существующего репозитория» главы 2, где рассматривается несколько примеров.

‰‰ В главе 4 мы показали вам, как с помощью параметра --bare создать копию

Git-репозитория без рабочей папки.

‰‰ В разделе «Пакеты» главы 7 мы пользовались этой командой для распаковки

Git-репозитория.

‰‰ Наконец, в разделе «Клонирование проекта с подмодулями» главы 7 мы пока-

зали, как параметр --recursive до некоторой степени упрощает клонирование
содержащего подмодули проекта.

По большому счету, эта команда фигурирует во всех главах, но мы перечислили
только более-менее нестандартные варианты ее применения.

Фиксация состояния
Для базового рабочего процесса, включающего индексирование содержимого и его
фиксацию в истории проекта, существует всего несколько команд.
git add
Команда git add перемещает из рабочей папки в область индексирования предназначенное для следующей фиксации содержимое. По умолчанию команда git
commit работает только с областью индексирования, поэтому именно команда
git add позволяет вам точно указать, что именно должно попасть в следующий
коммит.
Это одна из ключевых Git-команд, соответственно она упоминается в книге десятки
раз. Здесь же мы перечислим наиболее интересные варианты ее применения.
‰‰ Впервые команда появляется в разделе «Слежение за новыми файлами» главы 2,

там же описаны детали ее применения.

Фиксация состояния   481

‰‰ Мы упомянули, как эта команда используется для разрешения конфликтов

слияния в разделе «Конфликты при слиянии» главы 3.

‰‰ Эта команда позволила нам добавить в область индексирования только опреде-

ленные части измененного файла в разделе «Интерактивное индексирование»
главы 7.

‰‰ Наконец, мы эмулировали низкоуровневую работу этой команды в разделе

«Объекты-деревья» главы 10, чтобы дать вам представление о том, что скрыто
от ваших глаз.

git status
Команда git status показывает состояние файлов в рабочей папке и области
индексирования. С ее помощью вы можете узнать, какие файлы подверглись изменениям, но пока не индексировались, а какие уже индексировались, но пока не
зафиксированы. При обычном формате вывода также выводятся подсказки о том,
как изменить состояние файлов.
В первый раз эта команда и оба формата ее вывода — как основной, так и упрощенный — рассматривается в разделе «Проверка состояния файлов» главы 2. И хотя
эта команда неоднократно возникает в разных главах, практически все варианты
ее использования описаны именно тут.
git diff
Команда git diff служит для вычисления разницы между любыми двумя де­
ревьями. Это может быть разница между рабочей папкой и областью индексирования (собственно команда git diff), между областью индексирования и последним
коммитом (git diff --staged) или между двумя коммитами (git diff master
branchB).
‰‰ В первый раз мы воспользовались этой командой в разделе «Просмотр индек-

сированных и неиндексированных изменений» главы 2, демонстрируя, как
можно узнать, какие изменения уже находятся в области индексирования,
а какие еще нет.

‰‰ В разделе «Рекомендации по созданию коммитов» главы 5 перед фиксацией со-

стояния мы воспользовались этой командой с параметром --check для поиска
возможных проблем с пробелами.

‰‰ В разделе «Просмотр вносимых изменений» главы 5 мы научили вас эффективно

сравнивать ветки, применяя синтаксис git diff A...B.

‰‰ Мы воспользовались параметром -w, чтобы избавиться от проблем с пробелами,

а также показали, как параметры --theirs, --ours и --base позволяют сравнивать различные варианты конфликтующих файлов в разделе «Более сложные
варианты слияния» главы 7.

482    Приложение В • Git-команды
‰‰ Наконец, добавление к этой команде параметра --submodule позволило нам

в разделе «Подмодули» главы 7 сравнить изменения в подмодулях проекта.

git difftool
Команда git difftool просто запускает внешний инструмент для вывода на экран
разницы между двумя деревьями, когда вы хотите воспользоваться чем-то, отличным от встроенной команды git diff.
git commit
Команда git commit берет все содержимое файлов, проиндексированное с помощью
команды git add, и записывает в базу данных новый постоянный снимок состояния,
а затем сдвигает на этот снимок указатель ветки.
‰‰ С основами создания коммитов вы познакомились в разделе «Фиксация измене-

ний» главы 2. Там же мы продемонстрировали, как при помощи флага -a убрать
выполнение команды git add из рутинного рабочего цикла и как флаг -m передает сообщение фиксации в командную строку, избавляя вас от необходимости
пользоваться редактором.

‰‰ В разделе «Отмена изменений» главы 2 мы рассказали о параметре –amend, по-

зволяющем повторить последний коммит.

‰‰ В разделе «Суть ветвления» главы 3 было более подробно рассмотрено, какое

действие производит команда git commit и почему она функционирует именно
таким образом.

‰‰ Мы научили вас создавать зашифрованные подписи для коммитов, добавляя

к этой команде флаг -s в разделе «Подпись коммитов» главы 7.

‰‰ Наконец, в разделе «Объекты-коммиты» главы 10 мы детально рассмотрели,

как эта команда работает на низком уровне.

git reset
Команда git reset, как легко догадаться по ее названию, в основном используется
для отмены совершенных действий. Она перемещает указатель HEAD, в некоторых
случаях меняя область индексирования, а если вы воспользуетесь параметром
--hard, может внести изменения и в рабочую папку. В последнем случае существует
опасность потери данных, поэтому перед применением этого варианта команды
следует четко понимать, что и зачем вы делаете.
‰‰ Простейший пример применения команды git reset был исчерпывающе описан

в разделе «Отмена индексирования» главы 2, где с ее помощью мы убрали из
области индексирования файл, добавленный туда командой git add.

‰‰ В разделе «Команда reset» главы 7 мы детально объяснили все особенности

применения этой команды.

Ветвления и слияния   483

‰‰ Команда git reset --hard позволила нам остановить процедуру слияния в

разделе «Прерывание слияния» главы 7. Там же мы показали работу с командой git merge --abort, которая представляет собой своего рода оболочку для
команды git reset.

git rm
Команда git rm применяется для удаления файлов из области индексирования или
рабочей папки. Фактически ее действие обратно действию команды git add, ведь она
индексирует данные, которые будут удалены при следующей фиксации состояния.
‰‰ Более-менее подробно команда git rm рассмотрена в разделе «Удаление файлов»

главы 2, где мы продемонстрировали рекурсивное удаление файлов, а также удаление файлов из области индексирования при добавлении к команде параметра
--cached. В последнем случае в рабочей папке файлы сохраняются.

‰‰ Единственный альтернативный вариант применения команды git rm показан в

разделе «Удаление объектов» главы 10, где мы вкратце объяснили назначение
параметра --ignore-unmatch при выполнении команды git filter-branch,
которая просто не дает нам вывести сообщение об ошибке, если удаляемого
файла не существует. Эта возможность востребована при написании сценариев.

git mv
Команда git mv представляет собой всего лишь удобный инструмент перемещения файла, после которого для нового файла выполняется команда git add, а для
старого — команда git rm. Мы коротко рассмотрели ее в разделе «Перемещение
файлов» главы 2.
git clean
Команда git clean используется для удаления из рабочей папки ненужных файлов.
К таким файлам относятся временные артефакты сборки или файлы конфликтов
слияния. Различные параметры этой команды и сценарии ее применения мы рассмотрели в разделе «Очистка рабочей папки» главы 7.

Ветвления и слияния
За создание новых веток и их слияние друг с другом в системе Git отвечает всего
несколько команд.
git branch
Команда git branch, по сути, представляет собой инструмент управления ветками.
Она умеет выводить на экран список имеющихся у вас веток, создавать новые,
а также удалять и переименовывать их.

484    Приложение В • Git-команды
‰‰ Этой команде посвящена большая часть главы 3, поэтому там она использует-

ся повсеместно. Впервые она появляется в разделе «Создание новой ветки»,
а большинство ее функциональных возможностей (вывод списков и удаление)
рассматривается в разделе «Управление ветками».

‰‰ В разделе «Слежение за ветками» мы показали, как вариант команды git branch

-u используется в слежении за ветками.

‰‰ Наконец, мы рассмотрели, как эта команда функционирует на низком уровне

в разделе «Ссылки в Git» главы 10.

git checkout
Команда git checkout используется для перехода в другую ветку и выгрузки содержимого веток в рабочую папку.
‰‰ В первый раз она упоминается в разделе «Смена веток» главы 3 одновременно

с командой git branch.

‰‰ Мы показали, как с помощью флага --track инициировать слежение за веткой

в разделе «Слежение за ветками» главы 3.

‰‰ Мы использовали эту команду с параметром --conflict=diff3 для повторного

разрешения конфликтов файлов в разделе «Применение команды checkout»
главы 7.

‰‰ А в разделе «Команда reset» все той же главы 7 мы подробно рассмотрели связь

этой команды с командой git reset.

‰‰ Наконец, детали реализации этой команды были описаны в разделе «Указатель

HEAD» главы 10.

git merge
Команда git merge служит для вставки содержимого одной или нескольких веток
в ту ветку, в которой вы в данный момент находитесь. После этого команда двигает
текущую ветку к результату слияния.
‰‰ Впервые команда git merge появляется в разделе «Основы ветвления» главы 3.

После этого она в различных вариациях, но чаще всего в форме git merge , где указывается ветка, содержимое которой вы хотите слить, появляется
во всех главах.

‰‰ Мы рассказали вам, как слить все изменения в один коммит (система Git при

этом выполняет слияние таким образом, что в результате возникает один новый
коммит, не записывая историю ветки, куда вставляются данные), в самом конце
раздела «Открытый проект, ветвление» главы 5.

‰‰ В разделе «Более сложные варианты слияния» главы 7 мы подробно рассмо-

трели процесс слияния и данную команду, в том числе такой ее вариант, как

Ветвления и слияния   485

-Xignore-all-whitespace, а также флаг --abort, позволяющий прервать про-

блемное слияние.

‰‰ Мы научили вас проверять подписи перед процедурой слияния в проектах, ис-

пользующих GPG-ключи, в разделе «Подпись коммитов» главы 7.

‰‰ Наконец, в разделе «Слияние поддеревьев» все той же главы 7 вы познакомились

с процедурой слияния двух поддеревьев.

git mergetool
Команда git mergetool просто загружает вспомогательную программу при возникновении проблем со слиянием в системе Git.
Мы вкратце упомянули о ней в разделе «Конфликты при слиянии» главы 3, а также
подробно показали, как с ее помощью реализовать собственный инструмент слияния
в разделе «Внешние инструменты для слияния и индикации изменений» главы 8.
git log
Команда git log используется для отображения достижимой записанной истории
проекта, начиная с самого свежего коммита. По умолчанию она показывает только
историю ветки, в которой вы сейчас находитесь, но можно настроить ее на вывод
данных сразу о нескольких ветках. Кроме того, ее можно применять для индикации
различий между ветками на уровне коммитов.
Эта команда фигурирует практически во всех главах книги, демонстрируя нам
историю различных проектов.
‰‰ Мы познакомили вас с этой командой и более-менее подробно рассмотрели

механизм ее применения в разделе «Просмотр истории версий» главы 2. Вы
узнали, что параметры -p и --stat позволяют получить представление о том,
что нового появилось в каждом коммите, а параметры --pretty и --oneline
представляют историю более лаконично, особенно при выполнении фильтрации
по дате создания и автору.

‰‰ В разделе «Создание новой ветки» главы 3 мы воспользовались параметром

--decorate для индикации местоположения указателей веток в истории коммитов. А параметр --graph позволил нам посмотреть, как выглядит разошедшаяся

история.

‰‰ В разделах «Работа в маленькой группе» главы 5 и «Диапазоны коммитов»

главы 7 мы познакомили вас с синтаксисом branchA..branchB, позволяющим
команде git log выбирать только коммиты, присутствующие в одной ветке, но
отсутствующие в другой. В разделе «Диапазоны коммитов» этот прием применяется довольно интенсивно.

‰‰ В разделах «Протоколирование слияния» и «Три точки» главы 7 мы рассмо-

трели варианты синтаксиса branchA...branchB и --left-right, позволяющие

486    Приложение В • Git-команды
увидеть, что находится в одной или в другой ветке, но не в них обеих сразу.
Кроме того, в разделе «Протоколирование слияния» мы познакомили вас с параметром –merge, помогающим при отладке конфликтов слияния, и показали,
как с помощью параметра --cc увидеть конфликты слияния в истории проекта.
‰‰ В разделе «Сокращения журнала ссылок» главы 7 мы показали, как добавлени-

ем к команде параметра -g можно избавиться от необходимости обхода ветки,
а сразу вывести журнал ссылок в Git.

‰‰ В разделе «Поиск» главы 7 мы рассмотрели, как с помощью параметров -S и -L

реализуется достаточно сложный поиск в истории проекта. К примеру, было
показано, как посмотреть историю развития какой-либо функциональной возможности.

‰‰ В разделе «Подпись коммитов» главы 7 вы увидели, как с помощью параметра

--show-signature показать в выводе команды git log все коммиты со строкой

проверки в зависимости от того, корректно ли они подписаны.

git stash
Команда git stash позволяет на время скрыть незафиксированные наработки,
когда требуется освободить рабочую папку, а делать коммит для незавершенной
работы вам не хочется. Эта команда детально рассмотрена в разделе «Скрытие
и очистка» главы 7.
git tag
Команда git tag позволяет получить постоянную закладку на определенную точку
в истории кода. Как правило, она используется для таких вещей, как версии.
‰‰ Эта команда впервые появилась и была детально рассмотрена в разделе «Теги»

главы 2, а попрактиковались с ее применением мы в разделе «Идентификация
устойчивых версий» главы 5.

‰‰ Кроме того, мы научили вас создавать подписанные GPG-ключом теги с по­

мощью флага -s и проверять их с помощью флага -v в разделе «Подпись»
главы 7.

Совместная работа и обновление проектов
В системе Git не так уж много команд, требующих доступа к сети. Практически все
команды работают с локальной базой данных. Однако в ситуации, когда вы хотите
поделиться с коллегами результатами своего труда или скачать чужие наработки,
на помощь приходят команды для работы с удаленными репозиториями.

Совместная работа и обновление проектов   487

git fetch
Команда git fetch взаимодействует с удаленным репозиторием, скачивая оттуда
всю отсутствующую у вас информацию и сохраняя ее в локальной базе данных.
‰‰ Впервые мы показали вам эту команду в разделе «Извлечение данных из удален-

ных репозиториев» главы 2, а затем представили ряд примеров ее применения
в разделе «Удаленные ветки» главы 3.

‰‰ Кроме того, мы использовали эту команду в ряде примеров в разделе «Содей-

ствие проекту» главы 5.

‰‰ В разделе «Обращения к запросам на включение» главы 6 мы воспользовались

ею для извлечения конкретной ссылки, лежащей за пределами нашего рабочего
пространства, а в разделе «Пакеты» главы 7 вы узнали, как извлечь ссылку из
пакета.

‰‰ В разделе «Спецификация ссылок» главы 10 мы рассмотрели специальные ва-

рианты спецификации ссылок, позволяющие команде git fetch отклоняться
от предлагаемого по умолчанию поведения.

git pull
Команда git pull, по сути, представляет собой комбинацию Git-команд fetch и
git merge. То есть система Git сначала извлекает информацию из указанного вами
удаленного репозитория, а затем пытается вставить ее в текущую ветку.
‰‰ Кратко эта команда была описана в разделе «Извлечение данных из удаленных

репозиториев» главы 2, а в разделе «Просмотр удаленных репозиториев» этой же
главы было показано, как узнать, к каким изменениям приведет ее выполнение.

‰‰ Кроме того, в разделе «Перемещение после перемещения» главы 3 мы узнали,

как эта команда помогает решать проблемы с перемещениями.

‰‰ В разделе «Проверка удаленных веток» главы 5 мы показали, как использовать

эту команду с URL-адресом для извлечения изменений из удаленного репозитория без сохранения указанного адреса в списке.

‰‰ Наконец, в разделе «Подпись» главы 7 мы кратко упомянули, что параметр

--verify-signatures позволяет проверять зашифрованные GPG-ключом

подпи­си скачиваемых коммитов.

git push
Команда git push используется для взаимодействия с другим репозиторием,
вы­числения данных, которые есть в вашей локальной базе, но отсутствуют там,
и передачи этих данных в упомянутый репозиторий. Так как в данном случае
требуется право на запись в чужой репозиторий, команда прибегает к процедуре
аутентификации.

488    Приложение В • Git-команды
‰‰ В первый раз эта команда появилась в разделе «Отправка данных в удаленный

репозиторий» главы 2. Там мы рассмотрели основы отправки веток в удаленные
репозитории. В разделе «Отправка данных» главы 3 мы подробнее рассмотрели
специфику пересылки веток, а в следующем разделе — «Слежение за ветками» —
вы узнали, как настроить в отслеживаемых ветках автоматическую передачу
данных в удаленный репозиторий. В разделе «Ликвидация веток с удаленного
севера» все той же главы 3 мы воспользовались флагом --delete, чтобы с помощью команды git push убрать ветку с удаленного сервера.

‰‰ В разделе «Содействие проекту» главы 5 вы найдете ряд примеров применения

команды git push для публикации данных из веток в нескольких удаленных
репозиториях одновременно.

‰‰ В разделе «Обмен тегами» главы 2 мы показали, как эта команда после добавления

параметра --tags позволяет выкладывать в общий доступ созданные вами теги.

‰‰ В разделе «Публикация результатов редактирования подмодуля» главы 7 мы вос-

пользовались параметром --recurse-submodules, чтобы проверить возможность
публикации всех наших подмодулей перед их фактической отправкой на сервер.

‰‰ В разделе «Другие клиентские хуки» главы 8 мы коротко рассмотрели хук pre-

push, который представляет собой сценарий, запускаемый перед отправкой

данных на сервер, чтобы проверить допустимость этой отправки.

‰‰ Наконец, в разделе «Спецификация ссылок для отправки данных на сервер»

главы 10 мы рассмотрели процесс передачи данных с полной спецификацией
ссылок, а не с используемой обычно сокращенной версией. Это повышает точность выбора данных, которые вы хотите отправить в общий доступ.

git remote
Команда git remote представляет собой инструмент управления удаленными
репозиториями. Она позволяет сохранять длинные URL-адреса в виде понятных
коротких строк, например «origin», что избавляет вас от необходимости все время
вводить длинные адреса. У вас может быть несколько удаленных репозиториев,
а команда git remote дает возможность добавлять, редактировать и удалять их.
‰‰ Подробно эта команда рассматривается в разделе «Удаленные репозитории»

главы 2, где представлены процессы вывода списка удаленных репозиториев,
их добавления, удаления и переименования.

‰‰ Она появляется практически во всех последующих главах, причем всегда в одном

и том же стандартном формате git remote add .

git archive
Команда git archive создает архив файла из определенного снимка проекта. С ее
помощью мы создали tarball-архив проекта для передачи его по сети в разделе
«Подготовка устойчивой версии» главы 5.

Проверка и сравнение   489

git submodule
Команда git submodule используется для управления внешними репозиториями, вложенными в наши обычные репозитории. В роли таких вложенных
репози­ториев могут выступать библиотеки или другие ресурсы общего доступа.
У команды submodule есть ряд внутренних команд (add, update, sync и т. п.) для
управления такими ресурсами. Эта команда полностью описана в разделе «Подмодули» главы 7.

Проверка и сравнение
git show
Команда git show выводит Git-объекты на экран в простом и понятном пользователям виде. Обычно она применяется для получения информации о теге
или коммите.
‰‰ Впервые мы воспользовались ею в разделе «Теги с комментариями» главы 2 для

вывода сведений о теге, снабженном комментарием.

‰‰ Затем мы прибегли к этой команде в разделе «Выбор версии» главы 7, чтобы

отобразить коммиты в зависимости от выбранной версии.

‰‰ Одним из самых интересных примеров применения команды git show про-

демонстрирован в разделе «Слияние файлов вручную» главы 7, где показано,
как извлечь определенное содержимое файла на разных стадиях конфликта
слияния.

git shortlog
Команда git shortlog кратко воспроизводит вывод команды git log. Она принимает многие из параметров этой команды, но выводит не список всех коммитов,
а только их краткое описание, сгруппированное по авторам. В разделе «Команда
shortlog» главы 5 вы найдете пример создания с помощью этой команды аккуратной
сводки коммитов.
git describe
Команда git describe работает со всем, что можно трактовать как коммит, выводя удобную для чтения неизменяемую строку. Она создает описание коммита, что
является хотя и не такой однозначной, зато более понятной характеристикой, чем
контрольная сумма SHA.
Мы использовали команду git describe в разделах «Генерация номера сборки» и
«Подготовка устойчивой версии» главы 5 для генерации названия нашего файла
с новой версией.

490    Приложение В • Git-команды

Отладка
В системе Git есть команды, помогающие в процессе отладки искать проблемные
места в коде. Они позволяют не только отследить момент возникновения проблемы,
но и найти ее причину.
git bisect
Команда git bisect представляет собой чрезвычайно полезный инструмент отладки, позволяя путем автоматизированного двоичного поиска найти коммит,
в котором в первый раз возникла проблема. Она полностью рассмотрена в разделе
«Двоичный поиск» главы 7.
git blame
Команда git blame открывает примечания к файлу, указывающие, когда и кем в
последний раз редактировалась каждая строка. Она помогает найти человека, которому можно задать вопросы по поводу конкретного фрагмента кода. Детально
эта команда рассматривается в разделе «Примечания к файлам» главы 7.
git grep
Команда git grep позволяет легко найти строку или регулярное выражение в любом
файле вашего кода, в том числе в более старых версиях проекта. Она исчерпывающе
описана в разделе «Команда Git Grep» главы 7.

Исправления
В Git есть группа команд, рассматривающих коммиты с точки зрения вно­
симых ими изменений. Фактически цепочка коммитов воспринимается как
­цепочка исправлений. Именно в таком стиле эти команды помогают нам управлять ветками.
git cherry-pick
Команда git cherry-pick берет изменения, вносимые одним коммитом, и пытается
повторно применить их в виде нового коммита в текущей ветке. Эта возможность
полезна в ситуации, когда нужно взять из ветки один или два коммита, а не сливать
ветку целиком со всеми внесенными в нее изменениями. Этот процесс описывается
и демонстрируется в разделе «Схема с перемещением и отбором» главы 5.
git rebase
Команда git rebase представляет собой автоматизированную версию команды
cherry-pick. Она определяет набор коммитов и по очереди выбирает из них данные,
как бы перенося ветку на новое место.

Электронная почта   491

‰‰ Процедура перемещения детально рассмотрена в разделе «Перемещение данных»

главы 3. Коснулись мы и проблем, возникающих в процессе совместной работы
при переносе уже опубликованных веток.

‰‰ Практический пример применения этой команды рассмотрен в разделе «За-

мена» главы 7, где мы разбили историю проекта на два репозитория, попутно
воспользовавшись флагом --onto.

‰‰ Мы задействовали эту команду при разрешении конфликта слияния, возникшего

в процессе переноса в разделе «Команда rerere» главы 7.

‰‰ Кроме того, в разделе «Редактирование нескольких сообщений фиксации» гла-

вы 7 мы использовали эту команду в интерактивном сценарии, инициируемом
параметром -i.

git revert
Команда git revert, по сути, является антиподом команды git cherry-pick. Она
создает новый коммит, который действует диаметрально противоположно целевому
коммиту, отменяя все внесенные последним изменения. Мы воспользовались ею
в разделе «Отмена коммита» главы 7 для отмены результатов слияния.

Электронная почта
Множество использующих Git проектов, включая саму систему Git, активно поддерживаются через списки рассылки. В систему Git встроено несколько инструментов, облегчающих этот процесс, от генерации исправлений, которые можно легко
отправить по электронной почте, до применения этих изменений непосредственно
из папки «Входящие».
git apply
Команда git apply применяет исправления, созданные командой git diff или даже
командой GNU diff. Ее действие практически аналогично действию команды patch,
хотя есть и ряд мелких отличий. Процесс применения этой команды и обстоятельства, в которых она может потребоваться, мы описали в разделе «Исправления,
присланные по почте» главы 5.
git am
Команда git am используется для применения исправлений из ящика входящих
сообщений, особенно если он находится в формате mbox. Она требуется, когда вы
получаете исправления по электронной почте и хотите применить их к проекту.
‰‰ Применение команды git am, в том числе с параметрами --resolved, -i и -3,

рассмотрено в разделе «Команда am» главы 5.

492    Приложение В • Git-команды
‰‰ Существует набор хуков, которые могут оказаться полезными в рабочих схемах

с использованием команды git am. Все они рассмотрены в разделе «Хуки для
работы с электронной почтой» главы 8.

‰‰ Мы также воспользовались этой командой, чтобы применить исправления из

запроса на включения в формате GitHub в разделе «Уведомления по электронной почте» главы 6.

git format-patch
Команда git format-patch генерирует набор исправлений в формате mbox, которые можно отправить в список рассылки. Пример содействия проекту посредством
данной команды был рассмотрен в разделе «Открытый проект, электронная почта»
главы 5.
git send-email
Команда git send-email применяется для отправки по электронной почте исправлений, сгенерированных командой git format-patch. Пример вклада в проект с использованием этой команды вы найдете в разделе «Открытый проект, электронная
почта» главы 5.
git request-pull
Команда git request-pull генерирует пример тела сообщения для отправки. Если
у вас есть ветка на открытом сервере и вы хотите поделиться с кем-то результатами своего труда, не отправляя исправления по электронной почте, запустите эту
команду и пошлите получателю выводимые ею данные.
Процесс применения команды git request-pull мы продемонстрировали в разделе
«Открытый проект, ветвление» главы 5.

Внешние системы
В системе Git есть ряд команд для интеграции с другими системами контроля
версий.
git svn
Команда git svn используется для работы с Subversion-сервером. Это означает, что
вы можете использовать систему Git в качестве клиента для получения изменений
с Subversion-сервера и отправки туда собственных коммитов. Эта команда подробно
рассматривается в разделе «Git и Subversion» главы 9.

Администрирование   493

git fast-import
Для других систем контроля версий и для импорта произвольнымобразом отформатированных данных применяется команда git fast-import. Она быстро
преобразует данные в формат, понятный системе Git. Подробно она рассмотрена
в разделе «Другие варианты импорта» главы 9.

Администрирование
Для тех, кто занимается администрированием Git-репозиториев или хочет внести
какие-то глобальные исправления в проект, система Git предоставляет набор административных команд.
git gc
Команда git gc запускает в репозитории сборщик мусора, удаляя из базы данных
ненужные файлы, а все остальное упаковывая в более компактный формат. Обычно эта команда запускается автоматически, но при желании ее можно вызвать и
вручную. Примеры ее применения были рассмотрены в разделе «Обслуживание
репозитория» главы 10.
git fsck
Команда git fsck служит для проверки внутренней базы данных на наличие ошибок
и утрату целостности. Мы воспользовались ею всего один раз в разделе «Восстановление данных» главы 10 для поиска недостижимых ни по одной ссылке объектов.
git reflog
Команда git reflog просматривает во время вашей работы журнал положения
вершин веток с целью поиска коммитов, которые могут быть потеряны при перезаписи истории.
‰‰ В основном эта команда рассматривалась в разделе «Сокращения журнала

ссылок» главы 7, где мы показали стандартный вариант ее применения, а также
воспользовались вариантом git log -g для просмотра тех же самых данных,
которые предоставляет команда git log.

‰‰ На практике мы применили эту команду для восстановления утраченной ветки

в разделе «Восстановление данных» главы 10.

git filter-branch
Команда git filter-branch по заданному шаблону переписывает содержимое
коммитов, позволяя, к примеру, полностью удалить файл из истории или извлечь
во вложенную папку результаты фильтрации целого репозитория.

494    Приложение В • Git-команды
‰‰ В разделе «Удаление файла из всех коммитов» главы 7 мы объяснили механизм

работы этой команды и познакомились с такими ее параметрами, как --commitfilter, --subdirectory-filter и --tree-filter.

‰‰ В разделах «Мост Git-p4» и «TFS» главы 9 мы воспользовались этой командой

для приведения в нужный вид импортированных внешних репозиториев.

Служебные команды
В этой книге вы найдете и ряд низкоуровневых служебных команд.
‰‰ Во-первых, это команда ls-remote, с помощью которой в разделе «Обращения

к запросам на включение» главы 6 мы просматривали ссылки на сервере.

‰‰ Мы применяли команду ls-files в таких разделах главы 7, как «Слияние фай-

лов вручную», «Команда rerere» и «Индекс», чтобы понять, как на самом деле
выглядит область индексирования.

‰‰ Кроме того, в разделе «Ссылки из веток» главы 7 упоминается команда rev-

parse, позволяющая практически любую строку превратить в SHA-объект.

‰‰ Подавляющее большинство низкоуровневых служебных команд рассмотрено

в посвященной им главе 10. В других главах мы старались по возможности избегать применения этих команд.

Об авторах

Скотт Чакон — соучредитель и директор по инвестициям проекта GitHub. Также он являет­ся
администратором­официального сайта Git (git-scm.
com). Скотт Чакон принимал участие в десятках
конференций, посвященных Git, GitHub и будущему этих систем.

Бен Страуб — разработчик, долгое время участвовавший в проекте Libgit2, лектор и преподаватель
по Git международного уровня, книголюб, вечный
исследователь, человек, осваивающий искусство
создания прекрасного программного обеспечения.
С женой и двумя детьми он проживает в Портланде,
штат Орегон.

С. Чакон, Б. Штрауб

Git для профессионального программиста

Перевела на русский И. Рузмайкина
Заведующая редакцией
Ведущий редактор
Литературный редактор
Художник
Корректоры
Верстка

Ю. Сергиенко
Н. Римицан
А. Жданов
В. Шимкевич
Н. Викторова, В. Сайко
Л. Панич

ООО «Питер Пресс», 192102, Санкт-Петербург, ул. Андреевская (д. Волкова), д. 3, литер А, пом. 7Н.
Налоговая льгота — общероссийский классификатор продукции ОК 034-2014, 58.11.12 —
Книги печатные профессиональные, технические и научные.
Подписано в печать 07.09.15. Формат 70х100/16. Усл. п. л. 39,990. Заказ 0000