Git
Chapters ▾ 1st Edition

5.3 Распределённый Git - Сопровождение проекта

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

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

Работа с тематическими ветками

Если вы решаете, интегрировать ли новые наработки, как правило, неплохо было бы опробовать их в какой-нибудь временной тематической ветке, специально созданной для их тестирования. Так будет легче подправить отдельные патчи или забросить их до лучших времён, если что-то не работает. Если вы дадите ветке простое имя, основанное на теме содержащейся в ней работы, например, ruby_client, или как-нибудь так же наглядно, то вы сможете легко вспомнить, для чего эта ветка, если вам вдруг придётся отложить работу с ней и вернуться к ней позднее. В проекте 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 для применения патча, хотя она более параноидальна и допускает меньше нечётких совпадений, чем patch. К тому же она способна справиться с добавлением, удалением и переименованием файлов, описанными в формате git diff, чего команда patch сделать не сможет. И, наконец, git apply реализует модель "применить всё или ничего", тогда как 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

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

Применение патчей с помощью команды am

Если разработчик является достаточно хорошим пользователем Git'а и применил команду format-patch для создания своего патча, то ваша задача становится проще, так как такой патч содержит информацию об авторе и сообщение коммита. По возможности поощряйте участников проекта на использование команды format-patch вместо diff при генерировании патчей для вас. Команду git apply стоит использовать, только если нет другого выхода, и патчи уже созданы при помощи diff.

Чтобы применить патч, созданный при помощи format-patch, используйте команду git am. С технической точки зрения, git am читает mbox-файл, который является простым текстовым форматом для хранения одного или нескольких электронных писем в одном текстовом файле. Он выглядит примерно следующим образом:

From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
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

Это начало вывода команды format-patch, который мы уже видели в предыдущем разделе. Это одновременно и правильный mbox формат для e-mail. Если кто-то прислал вам по почте патч, правильно воспользовавшись для этого командой git send-email, и вы сохранили это сообщение в mbox-формате, тогда вы можете указать этот 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 и тела (до начала самого патча) электронного письма. Например, если применить патч из mbox-файла приведённого выше примера, то созданный для него коммит будет выглядеть следующим образом:

$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   add limit to log function

   Limit log functionality to the first 20

В поле Commit указан человек, применивший патч, а в CommitDate — время его применения. Информация 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, при использовании которой Git попытается выполнить трёхходовую операцию слияния. Эта опция не включена по умолчанию, так как она не работает в случае, если коммита, на котором был основан патч, нет в вашем репозитории. Если этот коммит всё же у вас есть — в случае когда патч был основан на публичном коммите — то опция -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 в интерактивном режиме — в этом случае команда останавливается на каждом найденном патче и спрашивает вас, хотите ли вы его применить:

$ 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 и имеющего свой собственный репозиторий, в который он и отправил свои изменения, а вам он прислал ссылку на свой репозиторий и имя удалённой ветки, в которой находятся изменения, то вы можете добавить его репозиторий в качестве удалённого и выполнить слияния локально.

Например, если Джессика присылает вам письмо, в котором говорится, что у неё есть классная новая функция в ветке ruby-client в её репозитории, вы можете протестировать её, добавив её репозиторий в качестве удалённого для вашего проекта и выгрузив содержимое этой ветки в рабочий каталог:

$ git remote add jessica git://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

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

Этот метод наиболее удобен, если вы работаете с человеком постоянно. Если кто-то изредка представляет вам по одному патчу, то менее затратно по времени будет принимать их по e-mail, чем заставлять всех иметь свои собственные репозитории и постоянно добавлять и удалять удалённые репозитории, чтобы получить пару патчей. Также вы, скорее всего, не захотите иметь у себя сотни удалённых репозиториев — для всех, кто предоставил вам один или два патча. Хотя сценарии и функции хостингов могут упростить эту ситуацию — всё зависит от того, как ведёте разработку вы и участники вашего проекта.

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

Если вы не работаете с человеком постоянно, но всё же хотите принять его изменения таким способом, можете указать URL его удалённого репозитория команде git pull. Так вы получите нужные изменения, а URL не будет сохранён в списке удалённых репозиториев:

$ git pull git://github.com/onetimeguy/project.git
From git://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by recursive.

Определение вносимых изменений

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

Часто полезно просмотреть все коммиты, которые есть в этой ветке, но нет в вашей ветке master. Исключить коммиты из ветки master можно добавив опцию --not перед именем ветки. Например, если участник вашего проекта прислал вам два патча, и вы создали ветку с именем contrib и применили эти патчи в ней, вы можете выполнить следующее:

$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    seeing if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    updated the gemspec to hopefully work better

Чтобы увидеть какие изменения вносит каждый коммит, если помните, можно передать опцию -p команде git log — к каждому коммиту будет добавлен его diff.

Чтобы посмотреть полный diff того, что добавится при слиянии вашей тематической ветки с другой веткой, вам может понадобиться использовать странный трюк, чтобы получить нужный результат. Вы, возможно, решите выполнить такую команду:

$ git diff master

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

Если master является прямым предком вашей тематической ветки, то проблем нет. Но если две линии истории разошлись, то diff будет выглядеть так, будто вы добавляете всё новое из вашей тематической ветки и удаляете всё уникальное в ветке master.

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

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

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

Однако это не очень удобно, так что в Git'е есть отдельное сокращённое обозначение для выполнения того же самого — запись с тремя точками. В контексте команды diff, вы можете поставить три точки после названия одной из веток, чтобы увидеть дельту между последним коммитом ветки, на которой вы находитесь, и их общим предком с другой веткой:

$ git diff master...contrib

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

Интегрирование чужих наработок

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

Процессы слияния

Один из простых рабочих процессов заключается в слиянии наработок в ветку master. В этом случае ваша ветка master содержит основную стабильную версию кода. Если у вас в тематической ветке находится работа, которую вы уже доделали, или полученные от кого-то наработки, которые вы уже проверили, вы сливаете её в свою ветку master, удаляете тематическую ветку, а затем продолжаете работу. Если в вашем репозитории наработки находятся в двух ветках, названия которых ruby_client и php_client (см. рис. 5-19), и вы выполняете слияние сначала для ветки ruby_client, в потом для php_client, то ваша история коммитов в итоге будет выглядеть, как показано на рисунке 5-20.


Рисунок 5-19. История коммитов с несколькими тематическими ветками.


Рисунок 5-20. История коммитов после слияния тематических веток.

Это, по всей видимости, наиболее простой рабочий процесс, но при работе с большими проектами здесь возникает ряд проблем.

Если ваш проект более крупный, или вы работаете с большим количеством разработчиков, вы, вероятно, будете применять по крайней мере двухэтапный цикл слияний. При этом сценарии у вас есть две долгоживущие ветки, master и develop, и вы решили, что ветка master обновляется только тогда, когда выходит очень стабильный релиз, а весь новый код включается в ветку develop. Изменения в обеих этих ветках регулярно отправляются в публичный репозиторий. Каждый раз, когда у вас появляется новая тематическая ветка для слияния (рисунок 5-21), вы сначала сливаете её в develop (рисунок 5-22); затем, когда вы выпускаете релиз, вы делаете перемотку (fast-forward) ветки master на нужный стабильный коммит ветки develop (рисунок 5-23).


Рисунок 5-21. История коммитов до слияния тематической ветки.


Рисунок 5-22. История коммитов после слияния тематической ветки.


Рисунок 5-23. История коммитов после появления релиза.

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

Рабочие процессы с крупными слияниями

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


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

Если тематические ветки требуют доработки, они сливаются в ветку pu. Когда будет установлено, что тематические ветки полностью стабильны, они переливаются в master, а ветки pu и next перестраиваются на основе тематических веток, находившихся в next, но ещё не дозревших до master. Это означает, что master практически всегда движется в прямом направлении, ветка next перемещается (rebase) иногда, а ветка pu перемещается чаще всех (см. рис. 5-25).


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

Когда тематическая ветка была полностью слита в ветку master, она удаляется из репозитория. В проекте Git есть ещё ветка maint, которая ответвлена от последнего релиза и предоставляет backport-патчи, на случай если потребуется выпуск корректировочной версии. Таким образом, когда вы клонируете Git-репозиторий, вы получаете четыре ветки, переключаясь на которые вы можете оценить проект на разных стадиях разработки (в зависимости от того, насколько свежую версию вы хотите получить, или от того, каким образом вы хотите внести в проект свою работу); а мейнтейнер, в свою очередь, имеет структурированный рабочий процесс, который помогает ему изучать новые присланные патчи.

Рабочие процессы с перемещениями и отбором лучшего

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

Другой вариант перемещения сделанных наработок из одной ветки в другую — отбор лучшего (cherry-pick). Отбор лучшего в Git'е является чем-то наподобие перемещения для отдельных коммитов. Берётся патч, который был представлен в коммите, и делается попытка применить его на ветке, на которой вы сейчас находитесь. Это удобно в том случае, если у вас в тематической ветке находится несколько коммитов, а вы хотите включить в проект только один из них, или если у вас только один коммит в тематической ветке, но вы предпочитаете выполнять отбор лучшего вместо перемещения. Например, предположим, ваш проект выглядит так, как показано на рисунке 5-26.


Рисунок 5-26. Пример истории коммитов перед отбором лучшего.

Если вы хотите вытащить коммит 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(-)

Эта команда включит в ветку master такие же изменения, которые были добавлены в e43a6, но вы получите новое значение SHA-1 для этого коммита, так как у него будет другая дата применения. Теперь ваша история коммитов выглядит, как показано на рисунке 5-27.


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

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

Отметка релизов

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

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

Если вы подписываете свои метки, у вас может возникнуть проблема с распространением открытого PGP-ключа, используемого для подписи ваших меток. Мейнтейнер проекта Git решил эту проблему, добавив свой публичный ключ в виде блоба (blob) прямо в репозиторий и затем выставив метку, указывающую прямо на содержимое ключа. Чтобы сделать это, определите, какой ключ вам нужен, выполнив gpg --list-keys:

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
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'е хранится ваш ключ, вы можете создать метку, напрямую указывающую на него, использовав значение SHA-1, возвращённое командой hash-object:

$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

Если вы запустите команду git push --tags, то метка maintainer-pgp-pub станет доступна каждому. Если кто-нибудь захочет проверить какую-нибудь метку, он сможет напрямую импортировать ваш PGP-ключ, вытащив блоб прямо из базы данных и импортировав его в GPG:

$ git show maintainer-pgp-pub | gpg --import

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

Генерация номера сборки

Так как коммитам в Git'е не присваиваются монотонно возрастающие номера наподобие 'v123' или чего-то аналогичного, то в случае, если вы хотите присвоить коммиту имя, удобное для восприятия, запустите команду git describe для этого коммита. Git вернёт вам имя ближайшей метки с числом коммитов, сделанных поверх этой метки и частичное значения SHA-1 описываемого коммита:

$ git describe master
v1.6.2-rc1-20-g8c5b85c

Таким образом, при экспорте снимка состояния проекта или его сборки вы можете дать им имя, понятное для людей. На самом деле, если вы собираете Git из исходного кода, склонированного из Git-репозитория, git --version вернёт вам что-то подобное. Если вы описываете коммит, которому вы напрямую присвоили метку, команда вернёт вам имя метки.

Команду git describe хорошо использовать с аннотированными метками (метками, созданными при помощи опций -a или -s), так что если вы используете git describe, то метки для релизов должны создаваться этим способом — в этом случае вы сможете удостовериться, что при описании коммиту было дано правильное имя. Вы также можете использовать эту строку в командах checkout и show для указания нужного коммита, однако в будущем она может перестать работать правильно в силу того, что в строке присутствует сокращённое значение SHA-1. Например, в ядре Linux недавно перешли от 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

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

Команда shortlog

Пришло время написать письмо для списка рассылки, чтобы поделиться новостями проекта со всеми, кто им интересуется. При помощи команды git shortlog можно быстро получить что-то наподобие лога изменений (changelog), описывающего, что появилось нового в вашем проекте со времени последнего релиза или последнего письма в список рассылки. Лог изменений включает в себя все коммиты в указанном диапазоне; например, следующая команда вернёт вам сводку по всем коммитам, сделанным со времени прошлого релиза (если последний релиз имел метку v1.0.1):

$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (8):
      Add support for annotated tags to Grit::Tag
      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, сгруппированным по авторам. Вывод этой команды можно послать в свой список рассылки.