Может ли ловушка 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будет работать, но не будет совершено.

Пример, который работает:

  1. Вы изменили файл и добавили его (например, файлgitignore)
      $> git status
On branch master

No commits yet

Changes to be committed:
  (use "git rm --cached <file>..." to unstage)
        new file:   .gitignore
  1. Запустите коммит. Во-первых,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
  1. Вы можете увидеть сообщение о том, что было зафиксировано. И теперь выводит:
      On branch master
nothing to commit, working tree clean

Пример, который НЕ работает

  1. Вам нечего добавить. Вы, конечно, можете запуститьgit addно это ничего не даст. дает:
      On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
  1. Запустите коммит. Крючок создастfile. Но в сообщении о коммите говорится, что он ничего не сделал.
      --- Dataset Pre-Commit Hook ---
--- Done ---
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean
  1. Проверьте статус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
  1. Вы можете видеть, что файл действительно был изменен и добавлен, но еще не зафиксирован. Вам нужно бежатьgit commitеще раз, чтобы это сработало.

Я попробовал добавить это какpost-commitкрючок, но не повезло. Если кто-то знает решение этого случая, я хотел бы знать.

Я столкнулся с той же проблемой в pre-commit hook также. Я изменял один файл и фиксировал, но он брал предыдущий файл, а не обновленный файл, поэтому, добавив команду git (как показано ниже) в ловушку pre-commit, это решило проблему.

git add $file

нота: $file Ваш файл будет добавлен.

Спасибо,

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