Ben Lynn
Август 2007
От редактора перевода
Не буду долго вас задерживать перед интересным чтением, лишь дам небольшие
пояснения по переводу терминологии.
Предисловие
1
вы постепенно поймете, как работает каждый трюк и как приспосабливать
рецепты под ваши нужды.
Благодарности
Я очень ценю, что столь многие люди работали над переводами этих строк. Я
благодарен названным выше людям за их усилия, расширившие мою аудиторию.
Если я случайно забыл упомянуть вас, пожалуйста, напомните мне или просто
вышлите патч.
2
• http://github.com/ хостинг для проектов с открытым кодом; а также для
закрытых проектов (на платной основе).
Лицензия
Это руководство выпущено под GNU General Public License 3-й версии.
Естественно, исходный текст находится в хранилище Git и может быть получен
командой:
Введение
Но это перезапишет старую версию. Это как в древних играх, где был только
один слот для сохранения: конечно, вы можете сохраниться, но вы больше
никогда не сможете вернуться к более раннему состоянию. Это досадно, так
как прежнее сохранение могло указывать на одно из очень интересных мест в
игре, и может быть, однажды вы захотите вернуться к нему. Или, что еще
хуже, вы сейчас находитесь в безвыигрышном положении и вынуждены начинать
заново.
3
Управление версиями
В системах управления версиями всё точно так же. У них у всех есть приятный
интерфейс для управления каталогом с вашим скарбом. Можете сохранять
состояние каталога так часто, как пожелаете, а затем восстановить любую
из предыдущих сохраненных версий. Но, в отличие от компьютерных игр,
они существенно экономят дисковое пространство. Обычно от версии к версии
изменяется только несколько файлов, и то ненамного. Хранение лишь различий
вместо полных копий требует меньше места.
Распределенное управление
4
кто-то забыл взять некий игровой предмет еще на третьем уровне, и нужно
найти последнее сохранение, где игру всё еще можно закончить. Или, может
быть, хочется сравнить две более старые сохраненные игры, чтобы установить
вклад конкретного игрока.
Может быть много причин вернуться к более старой версии, но выход один:
нужно запросить ту старую сохраненную игру у центрального сервера. Чем
больше сохраненных игр требуется, тем больше понадобится связываться с
сервером.
Глупые предрассудки
5
Конфликты при слиянии
Базовые операции
Сохранение состояния
$ git init
$ git add .
$ git commit -m "��� ������ ��������� �����"
Теперь, если новые правки всё испортили, можно восстановить первоначальную
версию:
6
Добавление, удаление, переименование
Расширенные отмена/возврат
$ git log
покажет список последних коммитов и их хеши SHA1:
commit 766f9881690d240ba334153047649b8b8f11c664
Author: Bob <bob@example.com>
Date: Tue Mar 14 01:59:26 2000 -0800
commit 82f5ea346a2e651544956a8653c0f58dc151275c
Author: Alice <alice@example.com>
Date: Thu Jan 1 00:00:00 1970 +0000
��������� ������.
Для указания коммита достаточно первых нескольких символов его хеша, но
можете скопировать и весь хеш. Наберите:
7
Возможно, в другой раз вы захотите быстро перескочить к старому состоянию.
В этом случае наберите
8
Откаты
$ git commit -a
$ git revert 1b6d
отменит коммит с заданным хешем. Откат будет сохранен в виде нового коммита.
Можете запустить git log, чтобы убедиться в этом.
Скачивание файлов
Если вы уже загрузили копию проекта с помощью git clone, можете обновить
ее до последней версии, используя
$ git pull
Безотлагательная публикация
9
проектом, но общедоступным они делают свой код только после того, как
приведут его в приличный вид.
Чтобы сделать это с помощью Git, выполните в каталоге, где лежит ваш
скрипт,
$ git init
$ git add .
$ git commit -m "������ �����"
Затем скажите вашим пользователям запустить
$ git pull
Ваши пользователи никогда не наткнутся на версию скрипта, которую вы не
хотите им показывать.
Что я сделал?
$ git diff
Или со вчерашнего дня:
10
медленное соединение. Как вариант, установите веб-сервер, введите git
instaweb и запустите любой веб-браузер.
Упражнение
$ git revert B
Какой способ лучше? Тот, который вам больше нравится. С помощью Git легко
получить желаемое, и часто существует много способов это сделать.
Все о клонировании
Синхронизация компьютеров
11
Создайте хранилище Git и закоммитьте файлы на одном компьютере. А потом
выполните на другом
$ git commit -a
$ git pull ������.���������:/����/�/������ HEAD
будут «втягивать» состояние файлов с другого компьютера на тот, где вы
работаете. Если вы недавно внесли конфликтующие изменения в один и тот
же файл, Git даст вам знать, и нужно будет сделать коммит заново после
разрешения ситуации.
$ git init
$ git add .
$ git commit -m "��������� ������"
На центральном сервере создайте так называемое «голое» (bare) хранилище
Git в неком каталоге:
$ mkdir proj.git
$ cd proj.git
$ git init --bare
$ # ������� «� ���� �������»: GIT_DIR=proj.git git init
Запустите Git-демон, если необходимо:
$ git commit -a
Для обновления до последней версии:
$ git pull
12
Любые конфликты слияния нужно разрешить и закоммитить:
$ git commit -a
Для выгрузки локальных изменений в центральное хранилище:
$ git push
Если на главном сервере были новые изменения, сделанные другими
разработчиками, команда push не сработает. В этом случае разработчику
нужно будет вытянуть к себе (pull) последнюю версию, разрешить возможные
конфликты слияний и попробовать еще раз.
Голое (bare) хранилище называются так потому, что у него нет рабочего
каталога. Оно содержит только файлы, которые обычно скрыты в подкаталоге
.git. Другими словами, голое хранилище содержит историю изменений, но не
содержит снимка какой-либо определенной версии.
Зачем вводится команда push, вместо использования уже знакомой pull? Прежде
всего, pull не работает в голых хранилищах, вместо нее нужно использовать
команду fetch, которая будет рассмотрена позже. Но даже если держать на
центральном сервере нормальное хранилище, использование команды pull в нем
будет затруднительным. Нужно будет сначала войти на сервер интерактивно и
сообщить команде pull адрес машины, с которой мы хотим забрать изменения.
Этому могут мешать сетевые брандмауэры (firewall), но в первую очередь:
что если у нас нет интерактивного доступа к серверу?
13
Создание форка проекта
$ git pull
Максимальные бэкапы
Если ваш проект не такой популярный, найдите как можно больше серверов для
размещения клонов.
14
Партизанское управление версиями
$ git init
$ git add .
$ git commit -m "��������� ������"
затем склонируйте его:
$ git add .
$ git commit -m "������������� � ����������"
Теперь перейдите в новый каталог и запустите
Mercurial
15
$ hg clone http://bitbucket.org/durin42/hg-git/
К сожалению, мне неизвестен аналогичное расширение для Git. Поэтому я
рекомендую использовать Git, а не Mercurial, для центрального хранилища,
даже если вы предпочитаете Mercurial. Для проектов, использующих Mercurial,
обычно какой-нибудь доброволец поддерживает параллельное хранилище Git для
привлечения пользователей последнего, тогда как проекты, использующие Git,
благодаря hg-git автоматически доступны пользователям Mercurial.
$ git init
$ hg-fast-export.sh -r /hg/repo
после добавления сценария в ваш $PATH.
Bazaar
16
Я также предпочитаю программы на C и сценарии на bash исполняемым файлам
вроде сценариев на Python-е: у них меньше зависимостей, и я привык к
быстрому выполнению.
Я думал о том, как можно улучшить Git, вплоть до того, чтобы написать
собственный инструмент, похожий на Git; но только как академическое
упражнение. Завершив проект, я бы все равно продолжил пользоваться Git,
потому что выигрыш слишком мал, чтобы оправдать использование самодельной
системы.
Чудеса ветвления
Решение: у Git есть более удобный инструмент для таких случаев, который
сэкономит и время, и дисковое пространство по сравнению с клонированием —
это git branch (branch — ветка, прим. пер.).
17
экспериментальной — на текущую версию в разработке, с нее — на версию
вашего друга и так далее.
Кнопка босса
В каком-нибудь каталоге:
Грязная работа
$ git commit -a
$ git checkout HEAD~3
Теперь вы можете добавлять временный черновой код в любых местах. Можно
даже закоммитить эти изменения. Когда закончите, выполните
18
чтобы вернуться к исходной работе. Заметьте, что любые изменения, не
внесенные в коммит, будут перенесены.
Быстрые исправления
Ваша работа в самом разгаре, когда вдруг выясняется, что нужно все бросить
и исправить только что обнаруженную ошибку в коммите «1b6d…»:
$ git commit -a
$ git checkout -b fixes 1b6d
После исправления ошибки сделайте
Слияния
19
локальных изменений, слияние произойдет само собой, как вырожденный случай
вроде получения последней версии в централизованной системе управления
версиями. Если же у вас есть локальные изменения, Git автоматически
произведет слияние и сообщит о любых конфликтах.
В производстве техники часто бывает, что второй шаг плана должен ждать
завершения первого шага. Автомобиль, нуждающийся в ремонте, может тихо
стоять в гараже до прибытия с завода конкретной детали. Прототип может
ждать производства чипа, прежде чем разработка будет продолжена.
20
Затем работайте над второй частью, попутно внося коммиты ваших изменений.
Человеку свойственно ошибаться, и часто вы хотите вернуться и поправить
что-то в первой части. Если вы везучи или очень искусны, можете пропустить
эти строки.
21
$ git cherry-pick medley^^
применит коммит «пра-родителя» головы ветки «medley» к ветке «sanitized».
Правильно подбирая элементы, вы сможете создать ветку, в которой будет лишь
окончательный код, а связанные между собой коммиты будут собраны вместе.
Управление Ветками
$ git branch
По умолчанию вы начинаете с ветки под названием «master». Кому-то нравится
оставлять ветку «master» нетронутой и создавать новые ветки со своими
изменениями.
Временные Ветки
$ git stash
Эта команда сохранит текущее состояние в во временном месте («тайнике»,
stash) и востановит предыдущее состояние. Ваш каталог становиться точно
таким, каким был до начала редактирования, и вы можете исправить ошибки,
загрузить удаленные изменения и тому подобное. Когда вы хотите вернуться
назад в состояние «тайника», наберите:
22
Можно создавать несколько тайников, используя их по-разному. Смотрите git
help stash. Как вы могли догадаться, Git оставляет ветки «за кадром» при
выполнении этого чудесного приема.
Уроки истории
Оставаясь корректным
Только что сделали коммит и поняли, что должны были ввести другое описание?
Запустите
23
чтобы изменить последнее описание. Осознали, что забыли добавить файл?
Запустите git add, чтобы это сделать, затем выполните вышеуказанную
команду.
…И кое-что еще
24
Локальные изменения сохраняются
Это работа для команды git rebase, описанной выше. Зачастую, имеет смысл
использовать флаг --onto и убрать переплетения.
Переписывая историю
Создавая Историю
25
то вполне вероятно, что кто-нибудь уже написал необходимые скрипты для
экспорта всей истории проекта в Git.
commit refs/heads/master
committer Alice <alice@example.com> Thu, 01 Jan 1970 00:00:00 +0000
data <<EOT
��������� ������.
EOT
int main() {
printf("Hello, world!\n");
return 0;
}
EOT
commit refs/heads/master
committer Bob <bob@example.com> Tue, 14 Mar 2000 01:59:26 -0800
data <<EOT
������� printf() �� write()
EOT
int main() {
write(1, "Hello, world!\n", 14);
return 0;
}
EOT
Затем создайте хранилище Git из этого временного файла при помощи команд:
26
$ git checkout master .
Команда git fast-export преобразует любое хранилище в формат, понятныый
команде git fast-import. Ее вывод можно использовать как образец для
написания скриптов преобразования, или для переноса хранилищ в понятном
человеку формате. Конечно, с помощью этих команд можно пересылать хранилища
текстовых файлов через каналы передачи текста.
27
Из-за кого все пошло не так?
Личный опыт
Я испытал этот феномен на себе. Git был моей первой системой управления
версиями. Я быстро привык к нему и стал относится к его возможностям как к
должному. Я предполагал, что и другие системы похожи на него: выбор системы
управления версиями не должен отличаться от выбора текстового редактора
или браузера.
Когда мне было нужно запустить медленную команду, нарушение хода моих
мыслей оказывало несоизмеримый ущерб разработке. Ожидая окончания связи
с сервером, я вынужден был заниматься чем-то другим, чтобы скоротать
28
время; например, проверкой почты или написанием документации. К тому
времени, как я возвращался к первоначальной задаче, выполнение команды было
давно закончено, но мне приходилось тратить уйму времени, чтоб вспомнить,
что именно я делал. Люди не очень приспособлены для переключения между
задачами.
Многопользовательский Git
Кто я?
29
Скачайте, скомпилируйте, установите Git в вашем аккаунте; создайте
хранилище в каталоге, доступном через web:
$ git push
���.������:/����/�/proj.git master
и кто угодно сможет взять ваш проект с помощью
30
Если это делается часто, можно легко забыть, какой коммит был отправлен
последним. Справка предлагает для решения этой проблемы использовать теги.
А именно, после передачи пакета введите
Патчи это тексты изменений, вполне понятные как человеку, так и компьютеру.
Это делает их очень привлекательным форматом обмена. Патч можно послать
разработчикам по электронной почте, независимо от того, какую систему
управления версиями они используют. Вашим корреспондентам достаточно
возможности читать электронную почту, чтобы увидеть ваши изменения. Точно
так же, с Вашей стороны требуется лишь адрес электронной почты: нет нужды
в настройке онлайн хранилища Git.
31
Для клиентов электронной почты, использующих mbox, есть небольшие отличия;
но если вы используете один из них, то вы, по всей видимости, можете легко
разобраться в этом без чтения описаний!
После клонирования хранилища команды git push или git pull автоматически
отправляют и получают его по первоначальному адресу. Каким образом Git это
делает? Секрет кроется в настройках, заданных при создании клона. Давайте
взглянем:
Удаленные ветки
$ git branch -r
Вы должны увидеть что-то вроде
32
origin/HEAD
origin/master
origin/experimental
Эти имена отвечают веткам и «голове» в удаленном хранилище; их можно
использовать в обычных командах Git. Например, вы сделали много коммитов,
и хотели бы сравнить текущее состояние с последней загруженной версией. Вы
можете искать в журналах нужный SHA1 хеш, но гораздо легче набрать
Держим в уме, что pull это просто fetch, а затем merge. Обычно мы используем
pull, потому что мы хотим влить к себе последний коммит после получения
чужой ветки. Описанная ситуация — примечательное исключение.
33
Мои Настройки
Гроссмейстерство Git
Релизы исходников
Коммит изменений
$ git add .
$ git add -u
Git просмотрит файлы в текущем каталоге и сам позаботится о деталях. Вместо
второй команды add, выполните git commit -a, если вы собираетесь сразу
34
сделать коммит. Смотрите git help ignore, чтобы узнать как указать файлы,
которые должны игнорироваться.
$ git add -p
Для каждой сделанной вами правки Git покажет измененный участок кода и
спросит, должно ли это изменение попасть в следующий коммит. Отвечайте
«y» (да) или «n» (нет). У вас есть и другие варианты, например отложить
выбор; введите «?» чтобы узнать больше.
$ git commit
для внесения именно тех правок, что вы выбрали («буферизованных» изменений).
Убедитесь, что вы не указали опцию -a, иначе Git закоммитит все правки.
35
Например, commit -a на самом деле двухэтапный процесс. Сначала слепок
текущего состояния каждого из отслеживаемых файлов помещается в индекс.
Затем слепок, находящийся в индексе, записывается в историю. Коммит без
опции -a выполняет только второй шаг, и имеет смысл только после выполнения
команд, изменяющих индекс, таких как git add.
Не теряй «головы»
Тег HEAD (англ. «голова», прим. пер.) — как курсор, который обычно
указывает на последний коммит, продвигаясь с каждым новым коммитом.
Некоторые команды Git позволяют перемещать этот курсор. Например,
Охота за «головами»
36
.git/objects и методом проб и ошибок найти нужный. Но есть путь
значительно легче.
$ git reflog
Вместо копирования хешей из reflog, попробуйте
Дизайн Git, в истинном духе UNIX, позволяет легко использовать его как
низкоуровневый компонент других программ: графических и веб-интерфейсов;
альтернативных интерфейсов командной строки; инструментов управления
патчами; средств импорта или конвертации, и так далее. Многие команды Git
на самом деле — скрипты, стоящие на плечах гигантов. Небольшой доработкой
вы можете переделать Git на свой вкус.
37
$ git config --global alias.co checkout
$ git config --global --get-regexp alias # ���������� ������� ������
alias.co checkout
$ git co foo # ��-��, ��� � «git checkout foo»
Другой пример: можно выводить текущую ветку в приглашении командной строки
или заголовке окна терминала. Запуск
Рискованные трюки
38
$ git branch -D �������_����� # ������ -d
Аналогично, попытка перезаписи ветки путем перемещения будет прервана,
если может привести к потере данных. Для принудительного перемещений
ветки введите
$ git clean -f -d
В следующий раз эта досадная команда сработает!
$ cd .git/hooks
$ cp pre-commit.sample pre-commit # � ������ ������� Git: chmod +x pre-commit
Теперь Git отменит коммит, если обнаружит лишние пробелы или неразрешенные
конфликты.
39
нужны Git для связи через не считающиеся с ним средства сообщения, такие
как HTTP.
Раскрываем тайны
Мы заглянем под капот и объясним, как Git творит свои чудеса. Я опущу
излишние детали. За более детальными описаниями обратитесь к руководству
пользователя.
Невидимость
Целостность
Так как SHA1 хеш сам является последовательностью байтов, мы можем получить
хеш строки байтов, содержащей другие хеши. Это простое наблюдение на
40
удивление полезно: ищите «hash chains» (цепочки хешей). Позднее мы увидим,
как Git использует их для эффективного обеспечения целостности данных.
Интеллект
Как Git узнаёт, что вы переименовали файл, даже если вы никогда не упоминали
об этом явно? Конечно, вы можете запустить git mv; но это то же самое,
что git rm, а затем git add.
Индексация
Мы отмечали ранее, что индекс это буферная зона. Почему набор свойств
файлов выступает таким буфером? Потому что команда add помещает файлы
в базу данных Git и в соответствии с этим обновляет эти свойства; тогда
как команда commit без опций создает коммит, основанный только на этих
свойствах и файлах, которые уже в базе данных.
Происхождение Git
41
База данных объектов
Каждый файл внутри .git/objects это «объект». Нас интересуют три типа
объектов: объекты «блобов», объекты деревьев и объекты коммитов.
Блобы
Для начала один фокус. Выберите имя файла — любое имя файла. В пустом
каталоге:
Таким образом, я могу легко предсказать, что вы увидите. Имя файла не имеет
значения: для создания блоб-объекта используется только его содержимое.
42
$ git cat-file -p aa823728ea7d592acc69b36875a482cdf3fd5c8d
что выведет указанный объект в читаемом виде.
Деревья
Этот файл — объект «дерево» (tree, прим. пер.): список цепочек, состоящих
из типа, имени файла и его хеша. В нашем примере: тип файла — 100644,
что означает, что «rose» это обычный файл; а хеш — блоб-объект, в котором
находится содержимое «rose». Другие возможные типы файлов: исполняемые
файлы, символические ссылки или каталоги. В последнем случае, хеш указывает
на объект «дерево».
$ rm -r .git/refs/original
$ git reflog expire --expire=now --all
$ git prune
43
Для реальных проектов обычно лучше избегать таких команд, поскольку вы
уничтожаете резервные копии. Если вы хотите иметь чистое хранилище,
то обычно лучше сделать свежий клон. Кроме того, будьте осторожны при
непосредственном вмешательстве в каталог .git: что если другая команда
Git работает в это же время, или внезапно произойдет отключение питания?
Вообще говоря, ссылки нужно удалять с помощью git update-ref -d, хотя
обычно ручное удаление refs/original безопасно.
Коммиты
Неотличимо от волшебства
44
приправленная блокировочными файлами и fsync для надеждности. По сути, это
точное описание ранних версий Git. Тем не менее, помимо гениальных трюков
с упаковкой для экономии места и с индексацией для экономии времени, мы
теперь знаем, как ловко Git преображает файловую систему в базу данных,
идеально подходящую для управления версиями.
Недостатки Git
45
Слабости SHA1
Microsoft Windows
Несвязанные файлы
Если ваш проект очень велик и содержит много несвязанных файлов, которые
постоянно изменяются, Git может оказаться в невыгодном положении по
сравнению с другими системами, поскольку отдельные файлы не отслеживаются.
Git отслеживает изменения всего проекта, что обычно бывает выгодным.
46
История файла
Потери как правило незначительны, и это неплохая цена за то, что другие
операции невероятно эффективны. Например, git checkout быстрее, чем cp -a,
а дельта всего проекта сжимается лучше, чем коллекция по-файловых дельт.
Начальное Клонирование
Изменчивые Проекты
Git был написан, чтобы быть быстрым при относительно небольших изменениях.
Люди вносят незначительные правки от версии к версии. Однострочное
исправление ошибки здесь, новая функция там, исправленные комментарии
и тому подобное. Но если ваши файлы радикально различаются в соседних
ревизиях, то с каждым коммитом ваша история неизбежно увеличится на размер
всего проекта.
47
так как неясно, зачем кому-либо понадобится история крайне нестабильных
файлов.
В этом случае исходный код стоит держать в хранилище Git, а бинарные файлы
— отдельно. Для упрощения жизни можно распространять скрипт, использующий
Git для клонирования кода и rsync или мелкий клон Git для прошивки.
Глобальный счетчик
Пустые подкаталоги
Первоначальный коммит
48
специальный коммит представлял бы собой пустое дерево, без родителей,
которое предшествует каждому хранилищу Git.
Причуды интерфейса
$ make tlh
$ firefox book.html
49
Почаще делайте коммиты, а когда ваш перевод будет готов, сообщите мне
об этом. На GitHub есть веб-интерфейс, облегчающий описанные действия:
сделайте форк проекта «gitmagic», залейте ваши изменения и попросите меня
сделать слияние.
50