- Могу ли я позвонить git внутри git хуков?
Да, но вы должны проявлять осторожность, так как есть ряд вещей установить в среде, и вы работаете с чем-то, что находится в середине выполнения:
GIT_DIR
задан путь к каталогу Git. GIT_WORKTREE
может быть установлено в путь к рабочему дереву (из git --work-tree
). - Другие переменные Git, такие как
GIT_NO_REPLACE_OBJECTS
, также могут быть установлены из командной строки.
(Вы должны оставить эти настройки, если продолжаете работать с текущим хранилищем, но очистить их, если вы работаете с другим хранилищем.)
Если 1. все в порядке: когда точно вызывается ловушка pre-commit, если я делаю git commit -am "bla"? В частности, git сначала делает постановку, а затем вызывает ловушку перед фиксацией или нет?
Это сложно.
Есть три "режима", которые git commit
использует внутренне. (Об этом нет никаких обещаний, но вот как все это реализовано в течение многих лет, поэтому эта трехрежимная работа кажется довольно стабильной.) Режимы:
git commit
без -a
, --include
, --only
и / или любые имена файлов, указанные в командной строке. Это стандартный или нормальный режим. Детали базовой реализации не отображаются через.
git commit
с -a
или с указанными в командной строке именами файлов. Это делит на два подрежима:
- такой коммит с
--include
, или - такой коммит с
--only
.
На этом этапе базовая реализация просвечивает.
Основные детали реализации здесь включают в себя то, что Git вызывает, по-разному, index , область подготовки и (редко в настоящее время) кэш , который обычно реализуется в виде файла с именем $GIT_DIR/index
(где $GIT_DIR
- переменная среды из примечания о точке 1) , Обычно есть только один из них: индекс . Он содержит контент, который вы намереваетесь зафиксировать. 1 Когда вы запускаете git commit
, Git будет упаковывать все, что является в индексом, как следующий коммит.
Но во время операции из git commit
может быть до трех индексных файлов. Для обычного git commit
есть только один индекс, и ваш хук перед фиксацией может использовать его и даже обновлять. (Я советую не обновлять его, по причинам, которые мы увидим через мгновение.)
Но, если вы сделаете git commit -a
или git commit --include file.ext
, теперь есть два индекса файлы. Есть контент, готовый для фиксации - обычный индекс - и один дополнительный индекс, который является исходным индексом плюс результат выполнения git add
для file.ext
или для все файлы (эквивалент git add -u
). Итак, теперь есть два индексных файла.
В этом режиме Git оставляет обычный индексный файл как обычный индексный файл. Этот файл в $GIT_DIR/index
как обычно. второй индексный файл с дополнительным добавленным содержимым находится в $GIT_DIR/index.lock
, а переменная окружения GIT_INDEX_FILE
установлена для хранения этого пути. Если фиксация не удалась , Git удалит файл index.lock
, и все будет так, как если бы вы вообще не запускали git commit
. Если фиксация завершится успешно , Git переименует index.lock
в index
, сняв блокировку и обновив (стандартный, обычный) индекс все в одном движении.
Наконец, есть третий режим, который вы получаете, например, при запуске git commit --only file.ext
. Здесь теперь есть три индексных файла:
$GIT_DIR/index
: стандартный индекс, который содержит то, что он обычно делает. $GIT_DIR/index.lock
: A копия стандартного индекса, для которого file.ext
был git add
-ед. $GIT_DIR/index<em>suffix</em>
: копия HEAD
commit 2 , для которого file.ext
был git add
-ed.
Переменная среды GIT_INDEX_PATH
указывает на этот третий индекс. Если фиксация завершится успешно, Git переименует файл index.lock
в index
, чтобы он стал индексом . Если фиксация завершится неудачно, Git удалит файл index.lock
, поэтому индекс вернется к состоянию, в котором он находился до того, как вы начали. (И в любом случае Git удаляет третий индекс, который теперь служит своей цели.)
Обратите внимание, что из ловушки перед фиксацией вы можете определить, является ли git commit
стандартным коммитом (GIT_INDEX_FILE
не установлено или установлено на $GIT_DIR/index
) или один из двух специальных режимов. В стандартном режиме, если вы хотите обновить индекс , вы можете сделать это как обычно. В двух специальных режимах вы можете использовать git add
для изменения файла с именами GIT_INDEX_FILE
, что изменит то, что входит в коммит; и если вы используете коммит в стиле * 1163, это также изменяет то, что станет стандартным индексом успеха. Но если вы находитесь в режиме --only
, изменение предложенного коммита не влияет на стандартные index
, и index.lock
, которые станут стандартный индекс.
Чтобы рассмотреть конкретный пример, предположим, что пользователь сделал:
git add file1 file2
, чтобы стандартный индекс совпадал с HEAD
, за исключением file1
и file2
, Затем пользователь запускает:
git commit --only file3
, так что предлагаемый коммит является копией HEAD
с добавлением file3
, и , если этот коммит завершится успешно, Git заменит стандартный индекс с индексом, в который добавляются file1
, file2
и file3
(но поскольку file3
будет соответствовать новому HEAD
коммиту, только файлы 1 и 2 будут модифицированы в новом индексе).
Теперь предположим, что хук фиксации запускает git add file4
и процесс в целом завершается успешно (новый коммит выполнен успешно). Шаг git add
скопирует версию рабочего дерева file4
во временный индекс, так что для фиксации будут обновлены file3
и file4
. Затем Git переименует файл index.lock
, чтобы file3
совпадал с новым HEAD
коммитом. Но file4
в index.lock
никогда не обновлялся, поэтому не будет соответствовать фиксации HEAD
. Пользователю покажется, что как-то file4
вернулось! git status
покажет ожидающие изменения в нем, подготовленные для фиксации, а git diff --cached
покажет, что разница между HEAD
и индексом заключается в том, что file4
был изменен обратно, чтобы соответствовать file4
в HEAD~1
.
Вы могли бы пройти предварительную проверку хука для этого режима и отказаться от git add
файлов в этом режиме, чтобы избежать проблемы. (Или вы могли бы даже незаметно добавить file4
к index.lock
с помощью второй команды git add
!) Но обычно лучше, чтобы ваш хук просто отклонял коммит, советуя пользователю делать любые git add
с сами по себе, так что вам не нужно знать все эти секреты реализации о git commit
в первую очередь.
1 Индекс содержит также некоторая дополнительная информация: данные кэша о рабочего дерева. Вот почему его иногда называют кешем. Эти дополнительные копии, которые я здесь описываю, создаются путем копирования исходного индекса, поэтому дополнительные копии также имеют те же данные кэша, за исключением случаев, когда они обновляются через git add
.
2 * 1242. * Не указано, будет ли Git делать эту копию с помощью внутреннего эквивалента:
TMP=$GIT_DIR/index<digits>
cp $GIT_DIR/index $TMP
GIT_INDEX_FILE=$TMP git reset
GIT_INDEX_FILE=$TMP git add file3
или каким-либо другим способом (например, внутренним эквивалентом git read-tree
), но так как эта конкретная копия всегда просто удалено в конце процесса, это не имеет значения: любая информация из кэша для рабочего дерева становится неактуальной.