Как я могу заполнить Git commit ID в файл, когда я фиксирую?
Я хотел бы создать Git hook(s), которые будут заполнять идентификатор коммита коммита, который я собираюсь сделать, в файл (в основном, подстановку переменных) в моем исходном коде. Это возможно с Git? Или это тот факт, что, разрешив переменную в git id, я собираюсь изменить sha 1, в результате чего возникнет проблема "курица или яйцо".
9 ответов
Решение, которое я использовал для аналогичной ситуации, таково:
- Положить строку
$Id$
где-то в файле, который вы хотите идентифицировать (например,test.html
), возможно, в комментариях или других нефункциональных разделах файла, где это не вызовет проблем. - В вашем
.gitattributes
пометить файл с помощьюident
ключевое слово (например,*.html ident
).
Результатом этого является то, что когда git checkout
копирует файл из базы данных объектов в рабочий каталог, расширяет $Id$
строка для чтения $Id: <sha-1 of file>$
, а также git add
отменяет это преобразование, когда вы хотите его зарегистрировать, поэтому версии этого файла в вашей объектной базе данных только когда-либо содержат $Id$
, не расширенные формы.
Это начало, но, к сожалению, найти коммит, содержащий файл с определенным хешем, не так просто, и не обязательно один-к-одному. Кроме того, я также помечаю эти файлы export-subst
атрибут (например, *.html ident export-subst
в .gitattributes
) и добавьте дополнительную строку, например $Format:%ci$ ($Format:%h$)
где-то в файле.
git checkout
а также git add
не затрагивайте эти теги, поэтому версии в моем репозитории всегда содержат именно эту строку. Чтобы расширить эти теги, вы должны использовать git archive
создать tar-шар (или.zip) определенной версии вашего проекта, который вы затем используете для развертывания этой версии - вы не сможете просто скопировать файлы или make install
или что угодно, так как git archive
это единственное, что расширит эти теги.
Два тега, которые я привел в качестве примера, расширяются до YYYY-MM-DD HH:MM:SS +TZOFFSET (HASH)
, где HASH
в данном случае это реальный хеш коммита, поэтому он более полезен.
Вы можете найти другие потенциально полезные $Format:$
Спецификаторы в git log
страница справки под --pretty-format
спецификаторы.
Невозможно делать то, что вы хотите: хеш коммита SHA-1 рассчитывается по всему снимку репозитория, включая каждый файл члена, поэтому возникает проблема с курицей и яйцами - для вычисления хеша коммитов вам нужно знать содержимое всех файлов, которые составь это.
Вы можете сделать это с post-commit
крюк. Вот выдержка из сайта git-scm
После завершения всего процесса фиксации запускается ловушка post-commit. Он не принимает никаких параметров, но вы можете легко получить последний коммит, запустив git log -1 HEAD. Обычно этот скрипт используется для уведомления или чего-то подобного.
Это был бы случай получения результата git log -1 HEAD
затем с помощью такого инструмента, как sed
заменить переменные в вашем файле. Тем не менее, это изменяет ваш рабочий каталог, и если вы не собираетесь выбросить эти изменения, вы получите постоянно измененный рабочий каталог.
Если вы просто хотите использовать текущий хеш коммита в переменной где-то в вашем коде, вы можете просто выполнить git log -1 HEAD
или же cat .git/HEAD
и сохранить вывод в вашей переменной
Если вам нужен только идентификатор (хэш), как в заголовке вопроса, вы можете использовать --format
флаг. git log -1 HEAD --format=%H
Вы можете создать фильтр для подстановки файлов при фиксации и извлечении. Они называются "грязными" и "чистыми" фильтрами, и их работа контролируется через .gitattributes
, Например:
*.c filter=yourfilter
Это говорит Git, чтобы запустить yourfilter
фильтр для всех .c
файлы. Затем вы должны сказать, что мерзавец yourfilter
средства:
git config --global filter.yourfilter.clean script1
git config --global filter.yourfilter.smudge script2
Затем вы должны написать скрипт (sed, Perl, Python или что-то еще), чтобы заменить выражение вроде $LastSha$
с $LastSha: <sha>$
на кассе ("пятно"). Другой сценарий отменяет расширение перед фиксацией ("чистый".)
Найдите в книге Pro Git "Расширение ключевого слова" для подробного примера.
Я искал ответ на этот вопрос. Идентификатор фиксации уже записан в файл, который вы просто должны знать, где искать. После выполнения коммита в основной ветке вы можете найти хеш коммита в
./.git/refs/heads/master
Поэтому в нашем решении для непрерывной доставки (оно загружает папку.git вместе с исходным кодом), мы можем просто
cat ./.git/refs/heads/${BRANCH}
чтобы связать текущий хеш коммита с нашей сборкой
Хорошо, вдохновленный ответом Джона Кэрнса, я придумал этот небольшой фрагмент, который вы могли бы вставить в ваш Makefile.
version.h:
git log -n 1 --format=format:"#define GIT_COMMIT \"%h\"%n" HEAD > $@
Это не совсем общее решение, но оно может пригодиться. Я знаю место или два, где я буду его использовать.
Как уже упоминали другие, вы не можете поместить SHA-1 самого коммита в файл во время того же коммита. В любом случае, это будет иметь ограниченное применение, поскольку при просмотре двух файлов вы не сможете сразу определить, какой из них более новый.
При этом фактически существует способ автоматически помещать информацию об отслеживании версий в зафиксированные файлы. Я сделал это для моего текущего проекта (FrauBSD; форк FreeBSD, над которым я работаю).
Я добился этого не с помощью фильтра git-attribute. В то время как фильтры git-атрибутов позволяют легко добиться обратного (поместить информацию в файл (-ы) при оформлении заказа), я хотел расширить некоторые ключевые слова во время фиксации, чтобы данные превратили их в хранилище (например, после "мастера отправления git push" github показывает расширенные значения в зафиксированном файле (ах). Достижение последнего оказалось чрезвычайно трудным с помощью фильтра git-атрибутов именно потому, что простой "git diff" вызовет атрибут filter.clean и, как было в моем случае, если вы помещаете информацию о дате / времени в расширение, имея изменение значения каждый раз, когда вы выполняете "git diff", нежелательно и недопустимо.
Поэтому я разработал ловушку перед фиксацией и ловушку commit-msg, которые, действуя вместе, решают проблему того, как (особенно в случае FrauBSD) заменить следующее в зафиксированных файлах:
$FrauBSD$
Что-то похожее на следующее перед регистрацией (расширенные значения идут вверх, чтобы другие могли оформить заказ):
$ FrauBSD: путь к файлу ГГГГ-ММ-ДД ЧЧ: ММ:ZZ Коммиттер GMTOFFSET $
Когда кто-либо просматривает файл на github или выполняет проверку или объединение файлов, расширенная информация распространяется на ходу.
ПРИМЕЧАНИЕ. Расширенное значение никогда не изменится, если не произойдет другое (не связанное) изменение, соответственно.
Например, см. Следующий коммит, в котором я просто удаляю завершающий символ новой строки файла. Фиксация содержит как удаление завершающего символа новой строки, так и удар по дате / времени в ключевом слове $FrauBSD$:
https://github.com/freebsdfrau/FrauBSD/commit/060d943d86bb6a79726065aad397723a9c704ea4
Чтобы создать этот коммит, я сделал то, с чем большинство разработчиков [git] знакомы:
- VI ЛИЦЕНЗИЯ
- Shift-G # перейти в конец файла
- dd # удалить текущую строку
- ZZ # сохранить файл и выйти
- git difF# diff показывает удаление завершающего символа новой строки. ПРИМЕЧАНИЕ: diff не показывает изменения значения $FrauBSD$ [пока]
- git add LICENSE
- git difF# nothing (без изменений)
- git diff --cached # diff показывает удаление завершающей строки: ПРИМЕЧАНИЕ: diff [still] не показывает изменение значения $FrauBSD$
- git status # показывает измененную ЛИЦЕНЗИЮ
- git commit # $ РЕДАКТОР подходит
- Ctrl-Z # положить $EDITOR в фоновом режиме, чтобы мы могли исследовать
- git diff --cached # diff [now] показывает обновление $FrauBSD$, а также удаление завершающей строки
- fg # резюме $ РЕДАКТОР
- : Д! # выход из редактора без изменений ПРИМЕЧАНИЕ. Поскольку вы прервали фиксацию, $FrauBSD$ был возвращен
- git diff --cached # diff [снова] показывает удаление только завершающего перевода строки
- git commit # на этот раз мы не будем прерывать
- BumpZZ # Вставить "Bump", сохранить и выйти
- Файл зафиксирован как есть
ПРИМЕЧАНИЕ. Ничего не нужно делать с файлом после фиксации.
Это потому, что у меня есть следующие файлы в моем проекте:
- .git/hooks/pre-commit (символическая ссылка на../../.hooks/pre-commit)
- .git / hooks / commit-msg (символическая ссылка на../../.hooks/commit-msg)
- .hooks / предварительной фиксации
- .hooks / фиксации-тзд
- .filters / fraubsd-ключевые слова
Первоначальные ревизии которых вы можете получить здесь:
"Добавить хуки / фильтры для предварительной смазки"
https://github.com/freebsdfrau/FrauBSD/commit/63fa0edf40fe8f5936673cb9f3e3ed0514d33673
ПРИМЕЧАНИЕ. Фильтры используются хуками (не используются в атрибутах git).
И обновление здесь:
HTTPS -//github.com/freebsdfrau/FrauBSD/commit/b0a0a6c7b2686db2e8cdfb7253aba7e4d7617432
Или вы можете просмотреть ревизии головы здесь:
HTTPS -//github.com/freebsdfrau/FrauBSD/tree/master/.filters
HTTPS -//github.com/freebsdfrau/FrauBSD/tree/master/.hooks
ПРИМЕЧАНИЕ: двоеточие изменено на - в приведенных выше URL, поэтому я могу разместить более 2 ссылок (так как репутация низкая)
Наслаждайтесь, FreeBSDFrau
Ключевым моментом здесь является помещение вашего идентификатора ревизии в какой-то файл, который не заботит Git. Вот фрагмент одного из моих проектов:
,,, AssemblyCS="Свойства /AssemblyInfo.cs" rev="$(git log -n 1 --date=short --format=format:"rev.%ad.%h" HEAD)" sed "$AssemblyCS",,,
Этот скрипт запускается как часть моего процесса сборки (это также может быть хук после фиксации). В этом случае Properties/AssemblyInfo.cs
в .gitignore
в то время как Properties/AssemblyInfo.cs.in
находится под контролем версий. Сборка использует .cs
файл, который содержит идентификатор редакции, который заканчивается в развернутом исполняемом файле.
Я искал нечто подобное, в том смысле, что мне нужна уникальная переменная, которую я мог бы добавить в конец файлов ресурсов (например, CSS/JS) в нашем интерфейсе, что позволило бы нам устанавливать очень длительное время кэширования, чтобы уменьшить пропускную способность и увеличить производительность, но легко заставить их перезагрузить после любого коммита. По существу, управление версиями файлов, но полностью автоматизировано. Мне было все равно, что это было САМОЕ последнее, поскольку оно было уникальным, автоматизированным и последовательным на всех наших серверах приложений.
Наш сценарий развертывания просто использует "git clone" для загрузки копии самого последнего кода на наши серверы приложений, но мы ограничиваем доступ через.htaccess к этим файлам и каталогам.
/.git/
каталог содержит файл с именем ORIG_HEAD
который обновляется после любого слияния (или любой другой опасной операции) с идентификатором фиксации его предшественника. Поскольку мы используем git flow, это идеально, потому что он обновляется каждый раз, когда мы нажимаем release
или fix
в главную ветку и разверните.
Вы можете сделать это, я полагаю, на любом языке сценариев, но в нашем случае, PHP, я сделал это так...
define("MY_VERSION",substr(file_get_contents(realpath(__DIR__.'/../.git/ORIG_HEAD')),0,3));
Ваш путь, очевидно, должен быть изменен для ваших собственных целей, но это приводит к тому , что для наших целей будет достаточно уникального идентификатора в 3 символа , который добавляется в конец URL нашего ресурса.
Надеюсь, что это помогает кому-то в такой же ситуации.