Можно ли повторно поместить файлы в ловушку git перед фиксацией?

Я сделал скрипт powershell, который добавляет заголовок к определенным файлам в моем проекте. Я хочу запускать его всякий раз, когда код находится в процессе отправки в GitHub, чтобы все такие файлы в репозитории GitHub имели присоединенный заголовок, что устраняет необходимость запуска сценария вручную.

Проблема заключается в том, что когда я изменяю файлы в своем git-хуке pre-commit, эти изменения не пересматриваются, поэтому мне, по сути, приходится ставить, фиксировать, а затем снова ставить, чтобы заголовки фиксировались.

Есть ли хороший способ справиться с этим, или лучше отделить выполнение сценария powershell от потока git?

2 ответа

TL;DR

Ответ на вопрос в строке темы - "да, но".:-) Я немного перефразирую это здесь, чтобы мы могли поговорить обо всех технических деталях. "Но" в том, что есть большой сюрприз, если вы используете git commit --only, Кажется, я помню, что было время, когда это git commit -a тоже плохо, но сейчас об этом лучше. (Я не уверен, какие версии Git ведут себя плохо с -a.)

Долго

Git делает каждый новый коммит из индекса или, в большинстве случаев, из индекса. Индекс или любой другой индекс, по сути, является предлагаемым коммитом: он состоит из своего рода сплющенного дерева с хэш-номерами больших двоичных объектов и именами файлов. Это в форме, которая делает его особенно удобным для git mktree, который превращает индекс в серию объектов дерева и возвращает хэш-идентификатор объекта дерева верхнего уровня, который затем переходит в объект фиксации.

Таким образом, вопрос затрагивает эти три части:

  1. Какой именно индекс используется для создания нового коммита?
  2. Можно ли изменить этот индекс? Если так, что происходит с новым коммитом?
  3. Как это влияет на индекс?

Здесь индекс - это специальный, выделенный индекс, который идет вместе с рабочим деревом. Существует один такой индекс для каждого рабочего дерева: если вы используете 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). Таким образом, каждый раз, когда ваш файл добавляется, скрипт запускается и вносит изменения, а затем фиксирует его.

Надеюсь, это поможет!

Другие вопросы по тегам