Глава 6. Многопользовательский Git

Сначала я использовал Git для личного проекта, в котором был единственным разработчиком. Среди команд, относящихся к распределенным свойствам Git, мне были нужны только pull и clone, чтобы хранить один и тот же проект в разных местах.

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

Кто я?

Каждый коммит содержит имя автора и адрес электронной почты, которые выводятся командой git log. По умолчанию Git использует системные настройки для заполнения этих полей. Чтобы установить их явно, введите

  $ git config --global user.name "John
Doe"
  $ git config --global user.email
johndoe@example.com

Чтобы установить эти параметры только для текущего хранилища, опустите флаг --global.

Git через SSH, HTTP

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

Скачайте, скомпилируйте, установите Git в вашем аккаунте; создайте хранилище в каталоге, доступном через web:

 $ GIT_DIR=proj.git git init
 $ cd proj.git
 $ git --bare update-server-info
 $ cp hooks/post-update.sample
hooks/post-update

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

$ chmod a+x hooks/post-update

Теперь вы можете публиковать свои последние правки через SSH с любого клона:

 $ git push
веб.сервер:/путь/к/proj.git master

и кто угодно сможет взять ваш проект с помощью

$ git clone http://веб.сервер/proj.git

Git через что угодно

Хотите синхронизировать хранилища без серверов или вообще без сетевого подключения? Вынуждены импровизировать на ходу в непредвиденной ситуации? Мы видели, как git fast-export и git fast-import могут преобразовать хранилища в один файл и обратно. Посредством обмена такими файлами мы можем переносить хранилища git любыми доступными средствами, но есть более эффективный инструмент: git bundle.

Отправитель создает пакет (bundle):

$ git bundle create некий-файл HEAD

Затем передает «пакет», некий-файл, другой команде любыми средствами, как то: электронная почта, флешка, xxd печать и последующее распознавание текста, надиктовка битов по телефону, дымовые сигналы и так далее. Получатель восстанавливает коммиты из пакета, введя

$ git pull некий-файл

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

В больших проектах для устранения излишков объема пакетируют только изменения, которых нет в других хранилищах. К примеру, пусть коммит «1b6d…» — последний общий для обеих групп:

$ git bundle create некий-файл HEAD ^1b6d

Если это делается часто, можно легко забыть, какой коммит был отправлен последним. Справка предлагает для решения этой проблемы использовать теги. А именно, после передачи пакета введите

$ git tag -f последний-пакет HEAD

и создавайте обновленные пакеты с помощью

$ git bundle create новый-пакет HEAD ^последний-пакет

Патчи: общее применение

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

Вспомним из первой главы:

$ git diff 1b6d

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

$ git apply < мой.patch

для применения патча.

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

$ git format-patch 1b6d

Полученные файлы могут быть отправлены с помощью git-send-email или вручную. Вы также можете указать диапазон коммитов:

$ git format-patch 1b6d..HEAD^^

На принимающей стороне сохраните письмо в файл и введите:

$ git am < email.txt

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

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

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

Приносим извинения, мы переехали

После клонирования хранилища команды git push или git pull автоматически отправляют и получают его по первоначальному адресу. Каким образом Git это делает? Секрет кроется в настройках, заданных при создании клона. Давайте взглянем:

$ git config --list

Опция remote.origin.url задает исходный адрес; origin — имя первоначального хранилища. Как и имя ветки master, это соглашение. Мы можем изменить или удалить это сокращённое имя, но как правило, нет причин для этого.

Если оригинальное хранилище переехало, можно обновить его адрес командой

$ git config remote.origin.url git://новый.url/proj.git

Опция branch.master.merge задает удаленную ветку по умолчанию для git pull. В ходе первоначального клонирования она устанавливается на текущую ветку исходного хранилища, так что даже если HEAD исходного хранилища впоследствии переместится на другую ветку, pull будет верно следовать изначальной ветке.

Этот параметр обращается только к хранилищу, которое мы изначально клонировали и которое записано в параметре branch.master.remote. При выполнении pull из других хранилищ мы должны указать нужную ветку:

$ git pull git://пример.com/other.git master

Это объясняет, почему некоторых из наших предыдущих примеров push и pull не имели аргументов.

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

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

Список удаленных веток можно посмотреть командой

$ git branch -r

Вы должны увидеть что-то вроде

origin/HEAD
origin/master
origin/experimental

Эти имена отвечают веткам и «голове» в удаленном хранилище; их можно использовать в обычных командах Git. Например, вы сделали много коммитов, и хотели бы сравнить текущее состояние с последней загруженной версией. Вы можете искать в журналах нужный SHA1 хеш, но гораздо легче набрать

$ git diff origin/HEAD

Также можно увидеть, для чего была создана ветка experimental:

$ git log origin/experimental

Несколько удаленных хранилищ

Предположим, что над нашим проектом работают еще два разработчика, и мы хотим следить за обоими. Мы можем наблюдать более чем за одним хранилищем одновременно, вот так:

$ git remote add other git://пример.com/некое_хранилище.git
$ git pull other некая_ветка

Сейчас мы сделали слияние с веткой из второго хранилища. Теперь у нас есть легкий доступ ко всем веткам во всех хранилищах:

 $ git diff origin/experimental^
other/некая_ветка~5

Но что если мы просто хотим сравнить их изменения, не затрагивая свою работу? Иными словами, мы хотим изучить чужие ветки, не давая их изменениям вторгаться в наш рабочий каталог. Тогда вместо pull наберите

 $ git fetch # Перенести из origin, по
умолчанию.
 $ git fetch other # Перенести от
второго программиста.

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

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

О том, как отключить удаленные хранилища, игнорировать отдельные ветки и многом другом смотрите в git help remote.

Мои Настройки

Я предпочитаю, чтобы люди, присоединяющиеся к моим проектам, создавали хранилища, из которых я смогу получать изменения с помощью pull. Некоторые хостинги Git позволяют создавать собственные форки проекта в одно касание.

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

Хотя со мной мало сотрудничают, я верю, что этот подход хорошо масштабируется. Смотрите эту запись в блоге Линуса Торвальдса.

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