Может ли ловушка Git автоматически добавлять файлы в коммит?
Я хотел бы добавить автоматически сгенерированный файл в тот же коммит, используя хук до или после фиксации в Git, в зависимости от файлов, которые были изменены в этом коммите. Как бы я пошел по этому поводу?
Я пробовал это как хук перед фиксацией, но не повезло:
#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
echo "Creating files"
exec bundle exec create_my_files
exec git add my_files
exec git commit --amend -C HEAD
fi
Это успешно добавляет их в репозиторий, но не добавляет их в коммит. Я также попытался использовать последние две строки exec в хуке post-commit вместе с проверкой перед фиксацией, но тоже ничего хорошего.
11 ответов
Так как git add также не работал для меня при предварительной фиксации, я следовал идее Марка об использовании файла.commit и разделении процесса на предварительную и последующую фиксацию.
Вот код, который должен быть легким для понимания
В предварительной фиксации:
- Нажмите файл.commit или что-то. (не забудьте добавить это в.gitignore)
#!/bin/sh
echo
touch .commit
exit
В пост-коммите:
если.commit существует, вы знаете, что фиксация только что состоялась, но пост-фиксация еще не началась Итак, вы можете сделать генерацию кода здесь. Кроме того, проверьте.commit и, если он существует:
- добавить файлы
- commit --amend -C HEAD --no-verify (избегать зацикливания)
- удалить файл.commit
#!/bin/sh
echo
if [ -a .commit ]
then
rm .commit
git add yourfile
git commit --amend -C HEAD --no-verify
fi
exit
Надеюсь, что это облегчает людям с небольшим знанием bash следовать идее Марка.
Можно делать то, что вы хотите, используя pre-commit hooks. Мы делаем нечто подобное для развертывания heroku (компилируем coffeescript в javascript). Причина, по которой ваш скрипт не работает, заключается в том, что вы использовали exec
команда неправильно
Со страницы руководства:
Встроенная функция exec используется для замены образа запущенного в данный момент процесса оболочки новой командой. При успешном завершении exec никогда не возвращается. exec нельзя использовать внутри конвейера.
Только ваша первая команда exec запущена. После этого ваш скрипт в основном прекращается.
Попробуйте что-то вроде этого (в качестве ловушки перед фиксацией):
#!/bin/sh
files=`git diff --cached --name-status`
re="<files of importance>"
if [[ $files =~ $re ]]
then
echo "Creating files"
bundle exec create_my_files
git add my_files
fi
#!/bin/sh
#
# .git/hooks/pre-commit
#
git add file.xyz
Это работало очень хорошо для меня. Это будет частью текущего коммита.
git version 1.7.12.4 (Apple Git-37)
Вы можете использовать комбинацию скрипта до и после коммита.
В предварительной фиксации:
- Нажмите файл.commit или что-то. (не забудьте добавить это в.gitignore)
В пост-коммите:
если.commit существует, вы знаете, что фиксация только что произошла, но пост-фиксация еще не запущена. Итак, вы можете сделать генерацию кода здесь. Кроме того, проверьте.commit и, если он существует:
- добавить файлы
- commit --ammend -C HEAD --no-verify (избегать зацикливания)
- удалить файл.commit
Это примерно тот процесс, который я использую для хранения файла.metadata в репозитории, сгенерированном из metastore.
Если кто-нибудь знает лучший способ, я все уши, но, кажется, работает на данный момент.
Ты можешь использовать update-index
:
git update-index --add my_files
Как насчет написания post-commit
вместо этого скрипт, который генерирует ваши файлы, а затем заставляет это делать (что-то вроде) git add my_files; git commit --amend
,
Если файлы генерируются автоматически, и они могут быть сгенерированы где угодно (подразумевается, что вы хотите встроить их в ловушку Git pre-commit), тогда вам не следует помещать их под контроль исходного кода. Вы должны контролировать только исходные файлы - сгенерированные файлы должны быть сгенерированы как часть сценариев сборки.
Единственная причина поставить созданный файл под контроль исходного кода - это когда для генерации требуются уникальные / привилегированные ресурсы (например, лицензионная программа) или для генерации требуется значительное количество времени.
добавленной
С http://git-scm.com/docs/githooks:
pre-commit Этот хук вызывается git commit, и его можно обойти с помощью опции --no-verify. Он не принимает никаких параметров и вызывается перед получением предложенного сообщения журнала фиксации и выполнением фиксации. Выход с ненулевым статусом из этого скрипта приводит к отмене коммита git.
Хук предварительной фиксации по умолчанию, если он включен, перехватывает введение строк с конечными пробелами и прерывает фиксацию при обнаружении такой строки.
Все ловушки git commit вызываются с помощью переменной среды GIT_EDITOR=: если команда не вызовет редактор для изменения сообщения фиксации.
Цель ловушки перед фиксацией состоит в том, чтобы перед выполнением фиксации пройти проверку на сбой состояния рабочего пространства и содержимого коммита. Попытка изменить содержимое коммита не будет работать.
Я рекомендую добавить два шага к вашим сценариям сборки: (1) шаг, который соберет все устаревшие файлы, которые необходимо сгенерировать (и добавит их в рабочую область), и (2) шаг, который проверит, чтобы все сгенерированные файлы были обновлены, и вернет ненулевой код состояния. Ваш Git pre-commit hook должен выполнить второй шаг. Ваши разработчики должны быть обучены запускать первый шаг по мере необходимости.
У меня была такая же потребность, и этот подход работал очень хорошо для меня:
#!/bin/sh
files='git diff --cached --name-only'
re="<files of importance>"
if [[ $files =~ $re ]]
then
echo "Creating files"
create_my_files && git add my_files
fi
где "create_my_files" должен быть исполняемым, например, если это файл python, вы можете выполнить его как "python create_my_files && git add my_files"
и это правда, вам не нужна предварительная фиксация для фиксации снова (это создаст бесконечный неприятный цикл:p)
Да, вы можете автоматически добавлять сгенерированные файлы в коммит, используя git hooks! Но это требует сложного сценария.
Здесь вы можете найти решенную проблему. Там он обновляет версию файла при каждом коммите, добавляет новый измененный файл и вносит изменения в коммит по мере необходимости. Он полностью работает: https://github.com/evandrocoan/.versioning
Затем вы просто заменяете алгоритм "Замена файла версии" в файле "updateVersion.sh" своим алгоритмом. Может быть, вам нужно изменить несколько вещей, например, удалить ограничение ветки, потому что там, сценарий запускается, только если вы находитесь в ветке 'development'.
Кроме того, он только изменит указанный файл, если он установлен. Если файл не является промежуточным, он ничего не сделает, кроме обычного / обычного коммита. Точнее, он распечатывает, что он делает на каждом шагу.
Я собираюсь объяснить этот трюк. Это довольно сложно. На методе prepare-commit-msg-hook он обнаруживает, находится ли нужный файл в стадии размещения и фиксации. После этого он создает файл флагов и останавливает подготовку-фиксацию-msg-hook. Позже, после post-commit-hook, он проверяет, существует ли файл флага. Если да, он исправляет файлы в коммите.
Внимание, это создаст бесконечный цикл, потому что он снова вызовет prepare-commit-msg-hook (как мы исправляем). Но это не происходит из-за файла флага. Когда prepare-commit-msg-hook запускается и находит файл флага, он "знает", что происходит. Затем просто удаляет файл флага и не создает его снова. Сделав это, он заблокирует post-commit-hook от повторного внесения изменений, что позволит завершить фиксацию навсегда.
Представьте, что у вас есть хук предварительной фиксации, который делает следующее:
#!/bin/bash
echo "--- Dataset Pre-Commit Hook ---"
DATASET_REPO_PATH="$(pwd)"
python3 -m my_script_that_generates_or_modifies_a_file
git -C $DATASET_REPO_PATH add file
echo "--- Done ---"
exit
Это будет работать ЕСЛИ И ТОЛЬКО ЕСЛИ уже было что-то, что нужно зафиксировать. Это не обязательно должен быть один и тот же файл. Если коммитить нечего, git addgit add file
будет работать, но не будет совершено.
Пример, который работает:
- Вы изменили файл и добавили его (например, файл
gitignore
)
$> git status
On branch master
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: .gitignore
- Запустите коммит. Во-первых,
pre-commit
Хук запустится и создаст . Тогда оно будет совершено.
--- Dataset Pre-Commit Hook ---
--- Done ---
[master (root-commit) 2ba2f7c] First commit
3 files changed, 34 insertions(+)
create mode 100644 .gitignore
create mode 100644 file
- Вы можете увидеть сообщение о том, что было зафиксировано. И теперь выводит:
On branch master
nothing to commit, working tree clean
Пример, который НЕ работает
- Вам нечего добавить. Вы, конечно, можете запустить
git add
но это ничего не даст. дает:
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
- Запустите коммит. Крючок создаст
file
. Но в сообщении о коммите говорится, что он ничего не сделал.
--- Dataset Pre-Commit Hook ---
--- Done ---
On branch master
Your branch is up to date with 'origin/master'.
nothing to commit, working tree clean
- Проверьте статус
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: file
- Вы можете видеть, что файл действительно был изменен и добавлен, но еще не зафиксирован. Вам нужно бежать
git commit
еще раз, чтобы это сработало.
Я попробовал добавить это какpost-commit
крючок, но не повезло. Если кто-то знает решение этого случая, я хотел бы знать.
Я столкнулся с той же проблемой в pre-commit hook также. Я изменял один файл и фиксировал, но он брал предыдущий файл, а не обновленный файл, поэтому, добавив команду git (как показано ниже) в ловушку pre-commit, это решило проблему.
git add $file
нота: $file
Ваш файл будет добавлен.
Спасибо,