linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
Intro
Lessons
Footer
linuxlab-УчебникиЦеныО платформеКонфиденциальность и куки
Copyright © 2026 LinuxLab. Все права защищены.
linuxlab.io
Учебники▾
  • Линукс и сети
    Файловая система, процессы, TCP/IP, BGP и OSPF
    →
  • Terraform и IaC
    HCL, state, plan/apply на sandbox LocalStack
    →
  • Git и GitHub
    Объектная модель, plumbing, ветвление, GitHub Actions
    →
Все учебники →
ЦеныО платформеВойтиСоздать аккаунт
/
  • Введение
  • Главы
  • How it works
  • Уроки
  • База знаний
  • Собеседование
home/git/lessons/git-lab-14-1-pre-commit-framework

lesson ── git-labs ── ~20 мин ── 9 шагов

pre-commit framework: автоматизация перед каждым коммитом

Цель - поднять pre-commit framework на маленьком репо, увидеть как hook блокирует «плохой» коммит, как hook автоматически правит файл и как --no-verify обходит проверку.

Sandbox air-gapped - до github.com не дойдёшь. В реальном проекте hooks обычно прилетают из публичных репозиториев (repo: https://github.com/...), здесь же используем repo: local с shell-командами. Идея и интерфейс framework те же, что и в проде.

pre-commit framework уже установлен в образе через pip (см. sandbox-images/git-base/Dockerfile).

▶ интерактивный sandbox

Поднимется контейнер gitlab/git-base с git, bash, pre-commit. В браузере откроется терминал, можно сразу git init. Каждый шаг проверяется автоматически. Сеть air-gapped, github.com недоступен.

запустить sandbox →

stack ── git · bash · 256 MB RAM · air-gapped · самоуничтожается через 30 мин простоя

Шаги

  1. 01

    Создай репо с парой плохо отформатированных файлов

    bash
    cd /home/student/work
    mkdir -p hooks-lab && cd hooks-lab
    git init -b main
    # двойные пробелы, нет пробела после ":" - типичные fmt-проблемы
    cat > config.yaml <<EOF
    port:    8080
    name:foo
    EOF
    # printf вместо echo - чтобы записать \n буквально, без трейлинг newline
    printf "name = 'world'\nprint('hi',name )\n" > app.py
    git add . && git commit -m "initial messy code"

    Файлы плохо отформатированы намеренно. Pre-commit это сейчас поймает.

    ✓ Репо с messy-файлами создано.

  2. 02

    Проверь, что pre-commit установлен

    bash
    which pre-commit              # путь до бинаря в $PATH
    pre-commit --version          # версия фреймворка

    Должно вывести путь и версию 4.0.1. Если не нашло - образ собран без python+pre-commit, пересобери sandbox-images/git-base/.

    подсказка

    `which` показывает путь до бинаря. `pre-commit --version` - его версию.

    ✓ pre-commit найден.

  3. 03

    Напиши .pre-commit-config.yaml с локальными hook'ами

    В air-gapped sandbox используем repo: local. Hook определяется целиком в конфиге: имя, команда, что делает. На реальном проекте ты бы написал repo: https://github.com/pre-commit/pre-commit-hooks и список id: оттуда - результат тот же.

    bash
    cd /home/student/work/hooks-lab
    cat > .pre-commit-config.yaml <<'EOF'
    repos:
      - repo: local
        hooks:
          - id: end-of-file-fixer
            name: ensure file ends with newline
            language: system
            entry: bash -c 'for f in "$@"; do [ -s "$f" ] && [ "$(tail -c1 "$f" | wc -l)" -eq 0 ] && echo "" >> "$f"; done' --
            types: [text]
          - id: trailing-whitespace
            name: strip trailing whitespace
            language: system
            entry: bash -c 'for f in "$@"; do sed -i "s/[[:space:]]*$//" "$f"; done' --
            types: [text]
          - id: forbid-fixme
            name: block FIXME in committed code
            language: system
            entry: bash -c 'for f in "$@"; do grep -nH "FIXME" "$f" && exit 1; done; exit 0' --
            types: [text]
    EOF
    git add .pre-commit-config.yaml
    git commit -m "add pre-commit config"

    Три hook'а: первые два чинят файл сами, третий ругается на FIXME и не пропускает коммит.

    ✓ Конфиг закоммичен.

  4. 04

    Активируй pre-commit для репо

    pre-commit install создаёт .git/hooks/pre-commit, который при каждом коммите будет запускать hooks из конфига.

    bash
    cd /home/student/work/hooks-lab
    pre-commit install            # создаёт .git/hooks/pre-commit -> запуск фреймворка
    ls .git/hooks/pre-commit

    Файл должен появиться.

    ✓ Hook зарегистрирован. Дальше попробуем коммит.

  5. 05

    Сделай коммит и увидь как pre-commit чинит файл

    У config.yaml нет финального переноса строки. end-of-file-fixer это поправит. Сначала измени файл, чтобы pre-commit увидел его staged:

    bash
    cd /home/student/work/hooks-lab
    printf 'name:foo' > config.yaml   # printf без \n - файл без финального newline
    git add config.yaml
    git commit -m "edit config"       # hook добавит newline, файл изменится, коммит отменится

    Pre-commit запустится, добавит перенос строки автоматически, пометит файл изменённым и отменит коммит. Это нормально: hook поправил, теперь нужен повторный git add.

    подсказка

    Если коммит прошёл - hook не сработал, проверь .pre-commit-config.yaml.

    ✓ pre-commit отработал. Файл поправлен, ждёт re-add.

  6. 06

    Перекоммить уже исправленный файл

    bash
    git add config.yaml           # пере-add уже исправленного hook'ом файла
    git commit -m "edit config"   # повторный hook не находит проблем, коммит проходит

    pre-commit запустится снова. Файл уже корректный, hook ничего не меняет, коммит проходит.

    Главное правило здесь видно в действии: hook идемпотентен - повторный запуск на исправленном файле ничего не делает.

    ✓ Коммит создан. Дальше - попробуем явный отказ.

  7. 07

    Попробуй закоммитить файл с FIXME и увидь отказ

    Третий hook (forbid-fixme) - не fixer, а блокирующий. Он не исправляет, он только не пропускает.

    bash
    cd /home/student/work/hooks-lab
    echo "# FIXME refactor later" >> app.py
    git add app.py
    git commit -m "wip: refactor app"

    Должен упасть с block FIXME in committed code .... Failed. Коммит не создан. Это второй паттерн pre-commit: некоторые проверки нельзя автоматически починить, и hook просто отказывает.

    подсказка

    Если коммит прошёл - либо в файле нет FIXME, либо hook сломан.

    ✓ Hook отказал, коммит заблокирован. Это и есть «защитный» режим.

  8. 08

    Обойди hook через --no-verify

    Иногда нужно срочно зафиксировать состояние без проверок. Это --no-verify:

    bash
    git commit --no-verify -m "wip emergency"   # --no-verify пропускает pre-commit hooks

    Коммит прошёл без запуска pre-commit. Это аварийный режим, не привычка. В нормальной работе hooks должны срабатывать.

    Правило: --no-verify для реальных emergency (срочный hotfix, перед которым надо что-то сохранить). Если регулярно ловишь себя на --no-verify - чини hook, а не привыкай его обходить.

    ✓ Обход через --no-verify увиден. Теперь финал.

  9. 09

    Прогон pre-commit вручную на всём дереве

    Когда добавляешь pre-commit в существующий репо, важно прогнать hooks по всем файлам разом - не ждать, пока кто-то по одному правит:

    bash
    pre-commit run --all-files    # --all-files = прогнать hooks по всему дереву, не только staged

    pre-commit пройдёт по всем файлам, чинимые автоматически - починит, нечинимые - подсветит. Закоммитишь одним PR «cleanup formatting».

    ✓ pre-commit прогнан по всему дереву. Урок пройден.

Что ты узнал

pre-commit живёт в .pre-commit-config.yaml, активируется одной командой pre-commit install, ловит проблемы локально до commit. Главное правило: hook должен быть быстрым и идемпотентным.

команды

  • pre-commit installсоздаёт .git/hooks/pre-commit, регистрирует framework
  • pre-commit run --all-filesпрогнать все hooks на всём дереве
  • git commit --no-verify -m '...'пропустить hooks (emergency only)

концепции

  • · hooks не клонируются - команда должна сделать `pre-commit install` после clone
  • · правило 5 секунд - иначе hooks обходят
  • · hook идемпотентен - повторный запуск не меняет файл

← предыдущая

git worktree: работать в двух ветках параллельно без stash

следующая →

Branch protection и CODEOWNERS в локальном forge

Footer
linuxlab-
Copyright © 2026 LinuxLab. Все права защищены.
Учебники
Цены
О платформе
Конфиденциальность и куки