Можно ли повторно поместить файлы в ловушку git перед фиксацией?
Я сделал скрипт powershell, который добавляет заголовок к определенным файлам в моем проекте. Я хочу запускать его всякий раз, когда код находится в процессе отправки в GitHub, чтобы все такие файлы в репозитории GitHub имели присоединенный заголовок, что устраняет необходимость запуска сценария вручную.
Проблема заключается в том, что когда я изменяю файлы в своем git-хуке pre-commit, эти изменения не пересматриваются, поэтому мне, по сути, приходится ставить, фиксировать, а затем снова ставить, чтобы заголовки фиксировались.
Есть ли хороший способ справиться с этим, или лучше отделить выполнение сценария powershell от потока git?
2 ответа
TL;DR
Ответ на вопрос в строке темы - "да, но".:-) Я немного перефразирую это здесь, чтобы мы могли поговорить обо всех технических деталях. "Но" в том, что есть большой сюрприз, если вы используете git commit --only
, Кажется, я помню, что было время, когда это git commit -a
тоже плохо, но сейчас об этом лучше. (Я не уверен, какие версии Git ведут себя плохо с -a
.)
Долго
Git делает каждый новый коммит из индекса или, в большинстве случаев, из индекса. Индекс или любой другой индекс, по сути, является предлагаемым коммитом: он состоит из своего рода сплющенного дерева с хэш-номерами больших двоичных объектов и именами файлов. Это в форме, которая делает его особенно удобным для git mktree
, который превращает индекс в серию объектов дерева и возвращает хэш-идентификатор объекта дерева верхнего уровня, который затем переходит в объект фиксации.
Таким образом, вопрос затрагивает эти три части:
- Какой именно индекс используется для создания нового коммита?
- Можно ли изменить этот индекс? Если так, что происходит с новым коммитом?
- Как это влияет на индекс?
Здесь индекс - это специальный, выделенный индекс, который идет вместе с рабочим деревом. Существует один такой индекс для каждого рабочего дерева: если вы используете git worktree add
вы получаете больше рабочих деревьев, и у каждого из них есть собственный закрытый индекс, но вы только когда-либо входите в одно рабочее дерево за раз, поэтому "индекс этого рабочего дерева" является индексом.
Тем не менее, когда вы запускаете git commit
, вы можете поручить это (используя --only
и / или другие аргументы командной строки) для создания собственного частного индекса, отдельного от индекса. Если вы сделали это, он будет запускать каждый из различных хуков с переменной окружения, GIT_INDEX_FILE
, установите путь к временному индексу. (Если не, GIT_INDEX_FILE
будет содержать .git/index
, который является путем к файлу индекса.)
Таким образом, ответ на вопрос 1: $GIT_INDEX_FILE
,
Теперь вы можете изменить любой индекс, который Git использует для создания коммита, потому что Git перечитает этот индекс после запуска ваших хуков. Таким образом, ответ на вопрос 2: Да, и это заставляет следующий коммит использовать то, что находится в каком бы индексе ни использовался.
Q3 самый сложный. Если вы используете git commit --only <paths>
Git должен сделать временный индекс, который:
- является копией
HEAD
- кроме указанного
<paths>
не нарушая индекс вообще. Если фиксация продолжается и завершается успешно, Git теперь должен изменить индекс, чтобы учесть тот факт, что эти <paths>
есть новые капли в них в новом HEAD
совершить.
Фактически, Git должен создать два временных индекса (индексы?), Один для предложенного коммита с его --only
файлы, и один для предложенного результата фиксации. Предлагаемый результат коммита становится index.lock
, который действует как новый индекс.
если ты git add
файлы во время работы они попадут во временный индекс. Но как насчет индекса? Давайте разберемся:
$ cat .git/hooks/pre-commit
#! /bin/sh
echo pre-commit hook, GIT_INDEX_FILE = $GIT_INDEX_FILE
git add sneaky
$ echo this is the base version of sneaky > sneaky
$ echo this is the base version of other > other
$ git add other sneaky
$ git commit -m 'create two files'
pre-commit hook, GIT_INDEX_FILE = .git/index
[master 5131b63] create two files
2 files changed, 2 insertions(+)
create mode 100644 other
create mode 100644 sneaky
Как вы можете видеть, ловушка предварительной фиксации сработала здесь и добавила sneaky
, В этом нет ничего страшного, поскольку то, что было скопировано в индекс для этого коммита, то есть в индекс, уже было той же базовой версией.
Теперь давайте изменим текущее содержимое sneaky
, git add
это, а также изменить текущее содержимое other
, а также git add
это, так что у нас есть новое содержимое в индексе для обоих файлов...
$ echo version 2 of other > other
$ echo version 2 of sneaky > sneaky
$ git add other sneaky
На этом этапе и индекс, и рабочее дерево имеют "версию 2". Теперь давайте обновим оба файла рабочего дерева до версии 3 без git add
их
$ echo version 3 of other > other
$ echo version 3 of sneaky > sneaky
$ git status --short
MM other
MM sneaky
Это говорит нам о том, что HEAD
, индекс и версии рабочего дерева различаются: HEAD
версия каждого является базовой, индексная версия - версия 2, а версия рабочего дерева - версия 3.
Теперь мы бежим git commit --only other
:
$ git commit --only other -m 'jump other straight to v3'
pre-commit hook, GIT_INDEX_FILE = [path]/.git/next-index-72393.lock
[master 91ec03b] jump other straight to v3
2 files changed, 2 insertions(+), 2 deletions(-)
Давайте посмотрим, что мы имеем сейчас в коммите, индексе и рабочем дереве:
$ git status --short
MM sneaky
ОК, версия дерева работ other
соответствует индексной версии other
который соответствует HEAD
версия other
, так:
$ cat other
version 3 of other
они все версии 3. Как насчет sneaky
, хоть? HEAD
и индекс отличается, а индекс и рабочее дерево различаются. Посмотрим что в HEAD
первый:
$ git show HEAD:sneaky
version 3 of sneaky
Теперь давайте посмотрим, что находится в рабочем дереве:
$ cat sneaky
version 3 of sneaky
Ага, эти совпадают! Самое сложное - просмотр индексной версии, но мы можем сделать это и с git show
:
$ git show :0:sneaky
version 2 of sneaky
Вау, посмотри на это! Индексная версия старая!
Это ответ на вопрос 3: git add
в хуке pre-commit обновляется тот индекс, который используется для построения следующего коммита, но если это временный индекс, он не обновляет то, что станет реальным индексом. Это возможно ошибка: git add
в хуке pre-commit следует также добавить индекс, который станет индексом. Сделать это немного сложно (git commit
мог прочитать временный индекс до и после перехвата и скопировать любые обновления в файл index.lock, возможно).
Обратите внимание, что я пропустил детали того, что произойдет, если вы совершите без --only
, Fortunately, in this case the index you add to is the index, so everything works as you would expect:
$ git reset --hard 5131b63
HEAD is now at 5131b63 create two files
$ echo v2 > other && echo v2 > sneaky && git add other
$ git commit -m 'regular commit'
pre-commit hook, GIT_INDEX_FILE = .git/index
[master 10a8b20] regular commit
2 files changed, 2 insertions(+), 2 deletions(-)
$ git status --short
$
The pre-commit git add
replaced the index version of sneaky
, and it stayed replaced... which it will even if the commit fails.
Note also that when using git commit -a
, we get a different temporary index:
$ git commit -a -m test
pre-commit hook, GIT_INDEX_FILE = [path]/.git/index.lock
Here, what Git does for git commit -a
is to create the proposed new index as the index.lock
файл. git add
proceeds normally, adding the file in the usual way, and then when the commit succeeds, Git renames index.lock
в index
, This unlocks the index file and, at the same time, puts the modified index in place, so that the answer for git commit -a
to Q3 is that the temporary index becomes the index. This is different from that for git commit --only
,
Это очень похоже на этот пост.
Вы хотите использовать .gitattributes
и укажите скрипты, которые запускаются во время добавления (т.е. git add
). Таким образом, каждый раз, когда ваш файл добавляется, скрипт запускается и вносит изменения, а затем фиксирует его.
Надеюсь, это поможет!