Files
local_machine/docs/git/GITEA_COMPLETE_GUIDE.md
ahauimix 1938b7c743 Expand local_machine docs and automation for connectivity, games, and ops.
Add proxy/VPN/telegram launchd and emergency runbooks; reorganize apps docs;
document JA3 CrossOver runbook and Wine troubleshooting; add GOG/HoMM game
scripts, disk cleanup guides, and gitea push-via-proxy helper. Ignore temp/.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-01 08:12:19 +03:00

82 KiB
Raw Blame History

Gitea Complete Guide — local_machine

Этот гайд — справочник по использованию Gitea для репозитория local_machine (документация и скрипты локальной машины).
Сервер: gitea.hunab.app (209.38.32.21).
Быстрый старт для local_machine: LOCAL_MACHINE_GITEA_SETUP.md.

Для других репозиториев: legal на 206.189.35.205 — GITEA_LEGAL_206_SERVER_GUIDE.md; hunabapp — свои скрипты пуша в том репо.


Дата: 2026-01-02
Версия Gitea: 1.25.3
Статус: Полностью развернут и работает
Последнее обновление: Адаптация под проект local_machine (2026-03-14)


📋 Обзор

Gitea успешно развернут на production сервере как self-hosted альтернатива GitHub.

Преимущества

  • Полный контроль данных (код на вашем сервере)
  • Бесплатно для неограниченного числа репозиториев
  • Нет лимитов на CI/CD минуты
  • Низкая задержка (локальная сеть)
  • Интеграция с существующей инфраструктурой

Текущая конфигурация

  • Домен: https://gitea.hunab.app
  • Сервер: 209.38.32.21
  • HTTP порт: 3000 (внутренний)
  • SSH порт: 2223 (внешний)
  • База данных: PostgreSQL (hunabgit) - СТАНДАРТ
  • SSL: Let's Encrypt, TLSv1.2/TLSv1.3
  • Контейнер: gitea
  • Сеть: hunab-network

⚠️ ВАЖНО: PostgreSQL является стандартом для всех развертываний Gitea. MySQL и SQLite не используются.


🚀 Развертывание

Быстрый старт для local_machine

Репозиторий local_machine не разворачивает Gitea — он только пушит в уже работающий сервер. Первая настройка и пуш:

cd /Users/eternal/code/local_machine
# 1. Создать репо на https://gitea.hunab.app (New Repository → local_machine)
# 2. Инициализация и пуш:
bash scripts/deployment/gitea/setup-and-push.sh

Подробно: LOCAL_MACHINE_GITEA_SETUP.md.

Развертывание самого Gitea (для админов сервера)

Скрипты развертывания Gitea на сервере находятся в других репозиториях (например hunabapp). Пример для справки:

# Развертывание Gitea (в репо с инфраструктурой)
bash scripts/deployment/gitea/deploy-gitea.sh
bash scripts/deployment/gitea/setup-gitea.sh
bash scripts/deployment/gitea/test-gitea.sh

Docker Compose (для reference)

version: "3"
services:
  gitea:
    image: gitea/gitea:1.25.3
    container_name: gitea
    restart: always
    networks:
      - hunab-network
    ports:
      - "3000:3000"
      - "2223:22"
    volumes:
      - /opt/gitea:/data
    environment:
      - USER_UID=1000
      - USER_GID=1000

🗄️ База данных PostgreSQL

⚠️ СТАНДАРТ (ОБЯЗАТЕЛЬНО К СОБЛЮДЕНИЮ): PostgreSQL является ЕДИНСТВЕННЫМ стандартом для всех развертываний Gitea. MySQL и SQLite ЗАПРЕЩЕНЫ.

PostgreSQL - ОБЯЗАТЕЛЬНЫЙ СТАНДАРТ

  • Используется на основном сервере (209.38.32.21)
  • Используется на зеркальном сервере (206.189.35.205)
  • ОБЯЗАТЕЛЕН для всех новых развертываний
  • ОБЯЗАТЕЛЕН для всех существующих развертываний (миграция с MySQL/SQLite)
  • Лучшая производительность для больших репозиториев
  • Поддержка транзакций и ACID
  • Масштабируемость и надежность

MySQL и SQLite - ЗАПРЕЩЕНЫ

  • ЗАПРЕЩЕНО использовать MySQL в проекте
  • ЗАПРЕЩЕНО использовать SQLite в production
  • Ошибка "dial tcp [::1]:3306" означает попытку подключения к MySQL - НЕДОПУСТИМО

Стандартная конфигурация

Основной сервер (209.38.32.21):

[database]
DB_TYPE  = postgres
HOST     = hunab-prod-postgres:5432
NAME     = hunabgit
USER     = hunabgit
PASSWD   = YOUR_PASSWORD
SSL_MODE = disable
CHARSET  = utf8mb4

Зеркальный сервер (206.189.35.205):

[database]
DB_TYPE  = postgres
HOST     = gitea-mirror-postgres:5432
NAME     = hunabgit
USER     = utils  # ⚠️ На зеркальном сервере используется пользователь utils
PASSWD   = YOUR_PASSWORD
SSL_MODE = disable
CHARSET  = utf8mb4

⚠️ КРИТИЧЕСКИ ВАЖНО:

  • Параметр PATH ЗАПРЕЩЕН (используется только для SQLite)
  • HOST = localhost:3306 ЗАПРЕЩЕН (это MySQL порт)
  • Обязательно использовать имя контейнера PostgreSQL в Docker сети
  • ⚠️ На зеркальном сервере используется пользователь utils вместо hunabgit

Создание пользователя utils для зеркального сервера

Проблема: Если пользователь utils не существует или имеет неправильный пароль, Gitea не сможет подключиться к PostgreSQL.

Решение:

Автоматический скрипт (рекомендуется)

# На сервере 206.189.35.205
bash scripts/deployment/fix-postgres-utils-user.sh

Ручное создание пользователя

# Определение суперпользователя PostgreSQL
POSTGRES_SUPERUSER=$(docker inspect gitea-mirror-postgres | grep -oP '(?<="POSTGRES_USER=")[^"]*' | head -1 || echo "postgres")

# Если не найден, пробуем стандартные варианты
if [ -z "$POSTGRES_SUPERUSER" ] || [ "$POSTGRES_SUPERUSER" = "null" ]; then
    if docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "SELECT 1;" > /dev/null 2>&1; then
        POSTGRES_SUPERUSER="postgres"
    elif docker exec gitea-mirror-postgres psql -U hunabgit -d postgres -c "SELECT 1;" > /dev/null 2>&1; then
        POSTGRES_SUPERUSER="hunabgit"
    else
        POSTGRES_SUPERUSER="postgres"
    fi
fi

# Создание или изменение пароля для utils
docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d postgres << 'SQL'
DO $$
BEGIN
    IF NOT EXISTS (SELECT FROM pg_user WHERE usename = 'utils') THEN
        CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD';
    ELSE
        ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD';
    END IF;
END
$$;
SQL

# Предоставление прав
docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;"
docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO utils;"
docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO utils;"
docker exec gitea-mirror-postgres psql -U $POSTGRES_SUPERUSER -d hunabgit -c "GRANT CREATE ON SCHEMA public TO utils;"

# Проверка
docker exec gitea-mirror-postgres psql -U utils -d hunabgit -c "SELECT version();"

Диагностика проблем:

  1. "role "utils" does not exist" - Пользователь не создан:

    docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD';"
    
  2. "password authentication failed" - Пароль неверный:

    docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD';"
    
  3. "permission denied for database" - Нет прав:

    docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;"
    

Проверка после создания:

docker exec gitea-mirror-postgres psql -U utils -d hunabgit -c "SELECT 1;"
# Должно вернуть: 1 без ошибок

Настройка через веб-интерфейс

При первом запуске http://209.38.32.21:3000:

Database Settings (СТАНДАРТ - PostgreSQL):

  • Database Type: PostgreSQL ⚠️ ОБЯЗАТЕЛЬНО PostgreSQL, НЕ MySQL, НЕ SQLite
  • Host: hunab-prod-postgres:5432 (для основного сервера) или gitea-mirror-postgres:5432 (для зеркального)
  • Database Name: hunabgit ⚠️ НЕ gitea!
  • Username: hunabgit (основной) или utils (зеркальный)
  • Password: YOUR_PASSWORD
  • SSL Mode: disable

General Settings:

  • Domain: gitea.hunab.app
  • SSH Port: 2223
  • HTTP Port: 3000
  • Gitea Base URL: https://gitea.hunab.app

Administrator Account:

  • Username: hunabgit
  • Email: mikhevel@gmail.com
  • Password: (надежный пароль)

🔐 SSL Configuration

Сертификат Let's Encrypt

# Получение сертификата
sudo certbot certonly --standalone -d gitea.hunab.app

# Копирование в рабочую директорию
sudo cp /etc/letsencrypt/live/gitea.hunab.app/fullchain.pem /opt/app/ssl/gitea-fullchain.pem
sudo cp /etc/letsencrypt/live/gitea.hunab.app/privkey.pem /opt/app/ssl/gitea-privkey.pem

Nginx Configuration

Конфигурация находится в docker/nginx/environments/gitea.conf:

# HTTP → HTTPS redirect
server {
    listen 80;
    server_name gitea.hunab.app;
    return 301 https://$host$request_uri;
}

# HTTPS server
# CRITICAL: HTTP/2 disabled - git operations require HTTP/1.1
server {
    listen 443 ssl;
    # http2 on;  # DISABLED: git operations don't work well with HTTP/2
    server_name gitea.hunab.app;
    
    # SSL configuration
    ssl_certificate /opt/app/ssl/gitea-fullchain.pem;
    ssl_certificate_key /opt/app/ssl/gitea-privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers HIGH:!aNULL:!MD5;

    # Gitea reverse proxy
    location / {
        proxy_pass http://gitea:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection 'upgrade';
        proxy_cache_bypass $http_upgrade;
        
        # Timeouts
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
        
        # Buffer settings
        proxy_buffering off;
        proxy_request_buffering off;
    }
    
    # Git operations (git-receive-pack, git-upload-pack) - увеличенные таймауты
    # CRITICAL: HTTP/2 disabled for git operations (git uses HTTP/1.1)
    location ~ ^/.*\.git/(git-receive-pack|git-upload-pack) {
        proxy_http_version 1.1;
        proxy_pass http://gitea:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        
        # CRITICAL: Use keep-alive for stable connection during data transfer
        proxy_set_header Connection "keep-alive";
        
        # Extended timeouts for git operations (для больших репозиториев)
        proxy_connect_timeout 300s;
        proxy_send_timeout 600s;
        proxy_read_timeout 600s;
        
        # Buffer settings (отключены для больших файлов)
        proxy_buffering off;
        proxy_request_buffering off;
        client_max_body_size 0;
        
        # Дополнительные настройки для стабильности
        proxy_redirect off;
        proxy_set_header Accept-Encoding "";
    }
    
    # Git info/refs operations - также с увеличенными таймаутами
    location ~ ^/.*\.git/info/refs {
        proxy_http_version 1.1;
        proxy_pass http://gitea:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Connection "";
        
        # Extended timeouts
        proxy_connect_timeout 300s;
        proxy_send_timeout 600s;
        proxy_read_timeout 600s;
        
        # Buffer settings
        proxy_buffering off;
        proxy_request_buffering off;
        client_max_body_size 0;
    }
}

Ключевые особенности:

  • HTTP/2 отключен для всего server блока (git требует HTTP/1.1)
  • Правильные SSL сертификаты (gitea-fullchain.pem с SAN для gitea.hunab.app)
  • Увеличенные таймауты для git операций (300-600s)
  • Отключена буферизация для больших файлов
  • Специальные location блоки для git операций

SSL Optimization

  • TLSv1.2 и TLSv1.3
  • Современные cipher suites
  • HSTS enabled (1 год)
  • Security headers
  • Session tickets disabled

Рекомендация: Добавьте DNS CAA запись:

Type: CAA
Name: gitea.hunab.app
Value: 0 issue "letsencrypt.org"

🔄 Push репозиториев (КРИТИЧЕСКИ ВАЖНО)

Для репозитория local_machine

В этом проекте пуш делается из корня local_machine:

cd /Users/eternal/code/local_machine
git push -u origin main

Если из РФ push по HTTPS зависает — переключите remote на SSH (порт 2223):

git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git
git push -u origin main

Если и SSH недоступен — используйте метод через bundle ниже. Полная настройка: LOCAL_MACHINE_GITEA_SETUP.md.


Проблема: Push через HTTPS зависает

При попытке git push через HTTPS возникают таймауты:

fatal: unable to access 'https://gitea.hunab.app/...': Recv failure: Operation timed out

Корневые причины:

  1. HTTP/2 несовместим с git операциями (git использует HTTP/1.1)
  2. Недостаточные таймауты для больших репозиториев
  3. Проблемы с keepalive соединениями через reverse proxy
  4. Ограничения curl/git на стороне клиента (особенно на macOS)

Диагностика в логах Gitea:

Fail to serve RPC(receive-pack): exit status 128 
fatal: the remote end hung up unexpectedly

POST запрос доходит до Gitea, но соединение обрывается во время передачи данных.

Универсальный скрипт пуша (в других репо)

В репозиториях с полной инфраструктурой (например hunabapp) используется скрипт, который автоматически выбирает метод (SSH → HTTPS → bundle):

# Обычный скрипт (для работы не из РФ)
bash scripts/deployment/gitea/git-push-gitea.sh [branch]

# 🇷🇺 ДЛЯ РФ: Скрипт через прокси (ускоряет в 5-10 раз)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]

Для local_machine такого скрипта нет — используйте git push origin main или SSH (см. выше).

🇷🇺 Из РФ:

  • Предпочтительно SSH (порт 2223) — часто стабильнее HTTPS.
  • Либо метод через bundle (см. раздел ниже).

Резервный способ: Временный скрипт для коммита и пуша

Когда использовать:

  • Терминал недоступен из-за проблем с zsh/shell
  • Агент не может выполнить команды напрямую
  • Нужно подготовить все команды заранее

Процесс:

  1. Агент создает временный скрипт в temp/commit-and-push.sh с полным коммитом и push командой
  2. Пользователь выполняет скрипт вручную в терминале
  3. Скрипт автоматически делает git add, git commit и git push

Формат временного скрипта (для local_machine):

#!/bin/bash
set -e
echo "📦 Committing changes..."
git add -A
echo "💾 Creating commit..."
git commit -m "docs: описание изменений"
echo "🚀 Pushing to Gitea..."
git push origin main
echo "✅ Done!"

Требования к скрипту:

  • Должен быть исполняемым (chmod +x)
  • Должен содержать полное сообщение коммита с описанием изменений
  • Для local_machine: git push origin main (или SSH remote, см. выше)
  • Должен быть в папке temp/ для временных скриптов
  • Должен иметь понятные echo сообщения для пользователя

Использование:

# Выполнить скрипт
bash temp/commit-and-push.sh

Очистка: После успешного выполнения скрипт можно удалить:

rm temp/commit-and-push.sh

⚠️ ВАЖНО:

  • Скрипт должен быть создан агентом ПЕРЕД запросом пользователя на коммит
  • Агент должен ВСЕГДА готовить скрипт, если терминал недоступен
  • Скрипт должен содержать ПОЛНОЕ описание изменений в коммите

Стандарт: без трейлеров в коммитах

В проекте не используются git-трейлеры в сообщениях коммитов (строки вида Key: Value в конце сообщения). Не добавлять:

  • Made-with: Cursor и аналогичные метки инструмента
  • Co-authored-by:, Signed-off-by: и т.п., если это не требуется явно политикой репозитория

Коммиты должны содержать только заголовок и при необходимости тело сообщения в читаемом формате.

Проверки перед коммитом (опционально)

Если в проекте есть проверка типов или линтеры — их можно запускать перед коммитом по желанию. Для local_machine (документация и скрипты) обычно достаточно git add и git commit.

(В проекте local_machine нет frontend/backend — при необходимости добавьте свои проверки в скрипты.)

Для других репо: проверка прав на .env после деплоя и typecheck-скрипты — в документации того проекта.

Метод 1: HTTPS с оптимизированными настройками

# Настройка git для больших push
git config http.postBuffer 524288000
git config http.lowSpeedLimit 0
git config http.lowSpeedTime 0
git config http.timeout 600

# Попытка push (для local_machine — ветка main)
git push origin main

⚠️ На macOS: HTTPS push часто не работает из-за отсутствия команды timeout. Скрипт автоматически пропускает HTTPS и использует bundle метод.

Метод 2: Bundle метод (надежный fallback)

Если HTTPS не работает, используется прямой доступ через файловую систему:

# Создать bundle (для local_machine — ветка main)
git bundle create /tmp/push.bundle origin/main..main

# Скопировать на сервер (hunab-prod — хост с доступом к Docker Gitea)
scp /tmp/push.bundle hunab-prod:/tmp/

# Применить в Gitea (репо local_machine)
ssh hunab-prod "docker cp /tmp/push.bundle gitea:/tmp/push.bundle && \
docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && \
git bundle unbundle /tmp/push.bundle && \
git update-ref refs/heads/main \$(git bundle list-heads /tmp/push.bundle | grep main | cut -d\" \" -f1) && \
rm /tmp/push.bundle' && rm /tmp/push.bundle"

Статус: Bundle метод всегда работает и обходит все проблемы с таймаутами.

Сравнение методов

Метод Скорость Надежность Сложность Платформа
SSH Быстро Высокая Просто Все
HTTPS (оптимизированный) Средне Средняя Просто Linux
Bundle (прямой доступ) Медленно Всегда работает ⚠️ Сложнее Все

Рекомендация: Используйте автоматический скрипт - он выберет лучший метод автоматически.

Миграция репозиториев

Для регулярных push в local_machine: git push origin main или SSH (см. раздел «Для репозитория local_machine» выше). В других репо может быть скрипт git-push-gitea.sh.

Шаг 1: Создание bundle из локального репозитория

cd /Users/eternal/code/local_machine
git bundle create /tmp/local_machine-migration.bundle --all

Шаг 2: Копирование bundle на сервер

scp /tmp/local_machine-migration.bundle hunab-prod:/tmp/local_machine-migration.bundle

Шаг 3: Распаковка bundle на сервере

ssh hunab-prod "cd /tmp && git clone --mirror local_machine-migration.bundle local_machine-mirror.git"

Шаг 4: Копирование в директорию Gitea

ssh hunab-prod "
  docker exec gitea mkdir -p /data/git/repositories/hunabgit/
  docker cp /tmp/local_machine-mirror.git gitea:/data/git/repositories/hunabgit/local_machine.git
  docker exec gitea chown -R git:git /data/git/repositories/hunabgit/local_machine.git
"

Шаг 5: Распаковка refs из packed-refs

КРИТИЧЕСКИ ВАЖНО: Gitea не видит ветки в packed-refs, нужно распаковать:

ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/local_machine.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'"

Шаг 6: Обновление базы данных

ssh hunab-prod "
  docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'local_machine';\"
  docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini
  docker restart gitea
"

Шаг 7: Проверка

# Проверка веток через API
curl -s -H "Authorization: token YOUR_TOKEN" \
  https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/branches | jq '.[] | .name'

# Проверка файлов через API (ветка main)
curl -s -H "Authorization: token YOUR_TOKEN" \
  https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/git/trees/main?recursive=0 | jq '.tree[] | .path'

# Веб-интерфейс
# https://gitea.hunab.app/hunabgit/local_machine

Результат: Репозиторий полностью мигрирован, все файлы доступны через веб-интерфейс и API.

Аутентификация

Создание токена доступа

  1. Откройте: https://gitea.hunab.app/user/settings/applications
  2. Generate New Token
  3. Название: api-access
  4. Права: write:repository, read:repository, read:user
  5. Скопируйте токен

Использование токена в git

# В локальном репозитории local_machine
cd /Users/eternal/code/local_machine
git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/local_machine.git

🏷️ Создание тегов и Release (СТАНДАРТ)

⚠️ КРИТИЧЕСКИ ВАЖНО: Gitea не обновляет индекс тегов автоматически

Проблема: Если создать тег напрямую в git репозитории (git tag -a v1.0.0), Gitea НЕ покажет его в веб-интерфейсе автоматически. Gitea кэширует список тегов и обновляет его только при создании Release через веб-интерфейс или API.

СТАНДАРТ: Создание тега через базу данных (РЕКОМЕНДУЕТСЯ)

Используйте этот метод для автоматического создания тегов:

# 1. Получить ID репозитория
REPO_ID=$(ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -t -c \"SELECT id FROM repository WHERE lower_name = 'local_machine';\" | tr -d ' '")

# 2. Создать Release в базе данных
ssh hunab-prod "docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"
INSERT INTO release (
  repo_id, 
  publisher_id, 
  tag_name, 
  lower_tag_name, 
  target, 
  sha1, 
  title, 
  note, 
  is_draft, 
  is_prerelease, 
  is_tag, 
  created_unix
) VALUES (
  $REPO_ID,
  1,
  'v1.3.7-CRM',
  'v1.3.7-crm',
  'dev',
  '1542c302c36ad367281f8d9740e4d9f4a817ad23',
  'v1.3.7-CRM: Полная реализация CRM Contacts Page',
  'feat(CRM): Полная реализация CRM Contacts Page v1.3.7',
  false,
  false,
  true,
  EXTRACT(EPOCH FROM NOW())::bigint
) ON CONFLICT (repo_id, tag_name) DO NOTHING
RETURNING id, tag_name, title;
\""

# 3. Перезапустить Gitea для обновления индекса
ssh hunab-prod "docker restart gitea"

Параметры:

  • repo_id - ID репозитория (получить через SELECT id FROM repository WHERE lower_name = 'local_machine';)
  • publisher_id - ID пользователя (обычно 1 для первого пользователя)
  • tag_name - Имя тега (например, v1.3.7-CRM)
  • lower_tag_name - Имя тега в нижнем регистре (например, v1.3.7-crm)
  • target - Ветка или commit (например, dev или 1542c302c36ad367281f8d9740e4d9f4a817ad23)
  • sha1 - SHA коммита (40 символов)
  • title - Заголовок Release
  • note - Описание Release
  • is_tag - true для тега, false для Release

Альтернатива: Создание через веб-интерфейс

Если нужно создать тег вручную:

  1. Откройте: https://gitea.hunab.app/hunabgit/local_machine/releases/new
  2. В поле "Tag" введите имя тега (например, v1.3.7-CRM)
  3. В поле "Target" выберите commit из списка или введите SHA
  4. Заполните Title и Description
  5. Нажмите "Publish Release"

⚠️ Недостаток: Требует ручного действия, не автоматизируется.

Альтернатива: Создание через API (требует токен)

# 1. Получить токен доступа (один раз)
TOKEN=$(ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin user generate-access-token --username hunabgit --scopes 'write:repository' --name 'api-tag-creation' 2>&1 | grep -o '[a-z0-9]\{40\}' | head -1")

# 2. Создать Release через API
curl -X POST "https://gitea.hunab.app/api/v1/repos/hunabgit/local_machine/releases" \
  -H "Authorization: token $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "tag_name": "v1.3.7-CRM",
    "target_commitish": "1542c302c36ad367281f8d9740e4d9f4a817ad23",
    "name": "v1.3.7-CRM: Полная реализация CRM Contacts Page",
    "body": "feat(CRM): Полная реализация CRM Contacts Page v1.3.7",
    "draft": false,
    "prerelease": false
  }'

⚠️ Недостаток: Требует токен доступа, который нужно создать заранее.

НЕ РАБОТАЕТ: Создание тега напрямую в git

# ❌ НЕ РАБОТАЕТ - Gitea не увидит тег в веб-интерфейсе
git tag -a v1.3.7-CRM -m "Message"
git push origin v1.3.7-CRM  # Даже после push тег не появится в веб-интерфейсе

Почему не работает:

  • Gitea кэширует список тегов в базе данных
  • Создание тега напрямую в git не обновляет таблицу release
  • Gitea не сканирует репозиторий автоматически для поиска новых тегов

📋 Чеклист создания тега

  • Получить ID репозитория из базы данных
  • Получить SHA коммита для тега
  • Создать Release в таблице release базы данных
  • Перезапустить Gitea для обновления индекса
  • Проверить что тег появился в веб-интерфейсе: https://gitea.hunab.app/hunabgit/local_machine/releases

🔧 Скрипт для автоматического создания тега

Готовый скрипт: scripts/deployment/gitea/create-gitea-tag.sh

🇷🇺 ДЛЯ РФ: Используйте прокси-версию для ускорения в 5-10 раз:

  • scripts/deployment/gitea/create-gitea-tag.sh - обычный скрипт
  • scripts/deployment/gitea/create-gitea-tag-via-proxy.sh - 🇷🇺 ДЛЯ РФ - через прокси

Использование:

# Обычный скрипт
bash scripts/deployment/gitea/create-gitea-tag.sh \
  <tag-name> \
  <commit-sha> \
  <title> \
  [description] \
  [branch]

# 🇷🇺 ДЛЯ РФ: Через прокси (рекомендуется из РФ)
bash scripts/deployment/gitea/create-gitea-tag-via-proxy.sh \
  <tag-name> \
  <commit-sha> \
  <title> \
  [description] \
  [branch]

Пример:

bash scripts/deployment/gitea/create-gitea-tag.sh \
  v1.3.7-CRM \
  1542c302c36ad367281f8d9740e4d9f4a817ad23 \
  "v1.3.7-CRM: Полная реализация CRM Contacts Page" \
  "feat(CRM): Полная реализация CRM Contacts Page v1.3.7" \
  dev

Что делает скрипт:

  1. Валидирует параметры (формат тега, SHA коммита)
  2. Получает ID репозитория из базы данных
  3. Создает Release в таблице release
  4. Перезапускает Gitea для обновления индекса
  5. Выводит ссылку для проверки

Особенности:

  • Автоматически обрабатывает конфликты (обновляет существующий тег)
  • Экранирует специальные символы в SQL
  • Проверяет существование репозитория
  • Выводит понятные сообщения об ошибках

📊 Мониторинг

Проверка статуса

# Контейнер
ssh hunab-prod "docker ps | grep gitea"

# Логи
ssh hunab-prod "docker logs gitea --tail 50"

# Ресурсы
ssh hunab-prod "docker stats gitea --no-stream"

# API
curl https://gitea.hunab.app/api/v1/version

# SSL
openssl s_client -connect gitea.hunab.app:443 -servername gitea.hunab.app </dev/null 2>&1 | grep -E 'Protocol|Cipher'

Проверка репозитория

# Ветки
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a | wc -l"

# Размер
ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/local_machine.git"

# Последний коммит
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git log -1 --pretty=format:'%H %s'"

🔧 Troubleshooting

Для детальной диагностики проблем см. GITEA_TROUBLESHOOTING.md - Полное руководство по решению проблем.

Для диагностики зеркального сервера см. GITEA_MIRROR_TROUBLESHOOTING.md.

Gitea не запускается

# Проверка логов
ssh hunab-prod "docker logs gitea --tail 100"

# Проверка портов
ssh hunab-prod "ss -tuln | grep -E '3000|2223'"

# Перезапуск
ssh hunab-prod "docker restart gitea"

Ветки не видны в веб-интерфейсе

Причина: Refs в packed-refs, Gitea их не читает

Решение: Распаковать refs (см. Шаг 5 миграции)

Статус: Проблема решена, все ветки видны в веб-интерфейсе

Git hooks кажутся сломанными

Проблема: Gitea показывает предупреждение "Git hooks of this repository seem to be broken"

Причина: После прямой миграции репозитория hooks могут быть не синхронизированы с Gitea

Решение: Регенерировать hooks для всех репозиториев:

ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini"

Проверка:

# Проверка прав на hooks
ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive"

# Проверка содержимого hooks
ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/local_machine.git/hooks/pre-receive.d/gitea"

Дополнительно: Если проблема сохраняется, проверьте:

  1. Файловая система поддерживает выполнение (chmod +x работает)
  2. Файловая система не смонтирована с noexec
  3. Docker версия >= 20.10.6 (у нас 28.0.1 )

См. документацию Gitea: https://docs.gitea.com/help/faq#push-hook--webhook--actions-arent-running

Статус: Решено через gitea admin regenerate hooks

Push через HTTPS зависает

Проблема: Git получает refs (GET запрос успешен), но POST запрос с данными не отправляется или соединение обрывается.

Причина:

  • Известная проблема с HTTP/2 и большими репозиториями в Gitea
  • Проблемы с keepalive соединениями через reverse proxy
  • Ограничения curl/git на стороне клиента (особенно на macOS)

Решение:

  1. Используйте универсальный скрипт: bash scripts/deployment/gitea/git-push-gitea.sh [branch]
  2. Скрипт автоматически выбирает лучший метод (SSH > HTTPS > Bundle)
  3. На macOS автоматически использует bundle метод (обходит проблемы с HTTPS)

Статус: Систематически решено через универсальный скрипт (2026-01-02)

SSL сертификат не обновляется

# Проверка автообновления
ssh hunab-prod "systemctl status certbot.timer"

# Ручное обновление
ssh hunab-prod "sudo certbot renew --dry-run"

# Копирование новых сертификатов
ssh hunab-prod "sudo cp /etc/letsencrypt/live/gitea.hunab.app/*.pem /opt/app/ssl/ && docker restart hunab-prod-nginx"

🔒 Безопасность

Рекомендации

  1. HTTPS через Let's Encrypt (настроено)
  2. Modern SSL/TLS protocols (TLSv1.2, TLSv1.3)
  3. Security headers (HSTS, X-Frame-Options, etc.)
  4. ⚠️ Firewall для ограничения доступа (рекомендуется)
  5. SSH ключи для git операций
  6. Регулярные бэкапы данных

Бэкапы

# Полный бэкап Gitea
ssh hunab-prod "
  tar -czf /opt/gitea-backup-$(date +%Y%m%d).tar.gz /opt/gitea
"

# Бэкап только репозиториев
ssh hunab-prod "
  tar -czf /opt/gitea-repos-backup-$(date +%Y%m%d).tar.gz /opt/gitea/git/repositories
"

# Бэкап базы данных
ssh hunab-prod "
  docker exec hunab-prod-postgres pg_dump -U hunabgit hunabgit > /opt/gitea-db-backup-$(date +%Y%m%d).sql
"

📈 Итоги и выводы

Что было достигнуто

  1. Gitea развернут на production (209.38.32.21)
  2. PostgreSQL настроен (отдельная БД hunabgit)
  3. SSL/TLS настроен (Let's Encrypt, A rating)
  4. Nginx reverse proxy (с оптимизацией)
  5. Репозиторий local_machine (и при необходимости другие репо) доступен на Gitea
  6. DNS настроен (gitea.hunab.app → 209.38.32.21)
  7. Веб-интерфейс работает (файлы отображаются корректно)
  8. API полностью функционален (все endpoints работают)

Ключевые проблемы и решения

Проблема 1: MySQL vs PostgreSQL

  • Проблема: Gitea пытался подключиться к несуществующему MySQL
  • Решение: Настроили PostgreSQL с отдельной БД

Проблема 2: SSL сертификат для поддомена

  • Проблема: Существующий сертификат был для hunab.app, не gitea.hunab.app
  • Решение: Получили новый сертификат через certbot --standalone

Проблема 3: Push через HTTPS зависает

  • Проблема: Git получает refs, но POST запрос с данными не отправляется или соединение обрывается
  • Причина: Известная проблема с HTTP/2 и большими репозиториями в Gitea, проблемы с keepalive соединениями
  • Попытки: Увеличение таймаутов Nginx до 600s, отключение HTTP/2, оптимизация Connection headers
  • Решение: Систематическое решение через универсальный скрипт git-push-gitea.sh (2026-01-02)
    • Автоматический выбор метода: SSH > HTTPS > Bundle
    • На macOS автоматически использует bundle метод
    • Всегда работает, обходит все проблемы с таймаутами

Проблема 4: Gitea не видит ветки

  • Проблема: Refs в packed-refs, Gitea их не индексирует
  • Решение: Распаковали refs в refs/heads/ структуру

Проблема 5: GitHub требует аутентификацию

  • Проблема: git clone --mirror с сервера не работает без токена
  • Решение: Использовали bundle, созданный локально

Lessons Learned

  1. Прямое копирование эффективнее push для больших репозиториев
  2. packed-refs не поддерживается Gitea — нужна распаковка
  3. certbot --standalone проще для новых сертификатов
  4. Docker network критичен для PostgreSQL подключения
  5. Bundle — универсальный способ переноса репозиториев

Производительность

  • Обычный git push через HTTPS: Зависает (POST запрос не отправляется или соединение обрывается)
  • Универсальный скрипт (git-push-gitea.sh): Автоматически выбирает лучший метод
    • SSH: Быстро (если доступен)
    • HTTPS: Средне (только на Linux)
    • Bundle: Медленно, но всегда работает (особенно на macOS)
  • 🇷🇺 ДЛЯ РФ: Прокси-версия (git-push-gitea-via-proxy.sh): Ускорение в 5-10 раз
    • Работает через российский прокси-сервер (149.154.64.19)
    • Стабильное соединение к Digital Ocean
    • Избежание таймаутов при работе из РФ
    • Bundle метод через прокси: Быстро и надежно
    • Мгновенная верификация - обновление tracking ref без сетевых запросов (2026-02-27)
  • Прямое копирование через bundle: ~1-2 минуты для инкрементальных обновлений
  • Полная миграция: ~5-10 минут (включая распаковку refs)
  • Размер репозитория: 556.8M (23 ветки, история с 2020 года)
  • Текущий статус: Все файлы доступны, веб-интерфейс работает корректно
  • Скрипт для push: scripts/deployment/gitea/git-push-gitea.sh - универсальное решение (2026-01-02)

Следующие шаги (опционально)

  1. Настроить CI/CD в Gitea (Gitea Actions)
  2. Настроить зеркалирование GitHub ↔ Gitea (для резервирования)
  3. Настроить SSH ключи для бесшовной работы
  4. Добавить DNS CAA запись для дополнительной безопасности
  5. Настроить автоматические бэкапы (cron job)
  6. Мигрировать deployment скрипты на использование Gitea

📚 Полезные команды

Для local_machine:

cd /Users/eternal/code/local_machine
git push origin main
# или SSH (если HTTPS таймаутит): git remote set-url origin ssh://git@gitea.hunab.app:2223/hunabgit/local_machine.git

Для админов сервера (Gitea Admin):

docker exec gitea /usr/local/bin/gitea admin --help
docker exec gitea /usr/local/bin/gitea admin regenerate hooks

# Git операции в Gitea (репо local_machine)
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git branch -a
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/local_machine.git log --oneline -10

# PostgreSQL операции
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c "SELECT * FROM repository;"

# Проверка конфигурации базы данных
docker exec gitea cat /data/gitea/conf/app.ini | grep -A 10 '\[database\]'
# Должно быть: DB_TYPE = postgres, HOST = <postgres-container>:5432

# SSL проверка
openssl s_client -connect gitea.hunab.app:443 -servername gitea.hunab.app
curl -vI https://gitea.hunab.app 2>&1 | grep -E 'SSL|TLS'

# Nginx
docker exec hunab-prod-nginx nginx -t
docker logs hunab-prod-nginx --tail 50

# Проверка логов Gitea при push
ssh hunab-prod "docker logs gitea --tail 50 | grep -E '(receive-pack|upload-pack|error)'"

🔄 Зеркалирование

Для полного руководства по зеркалированию см. GITEA_MIRRORING_GUIDE.md.

Миграция Gitea на зеркальный сервер

📋 План миграции: См. GITEA_MIGRATION_TO_MIRROR_SERVER.md - Детальный план миграции Gitea с production сервера (209.38.32.21) на зеркальный сервер (206.189.35.205).

Причины миграции:

  • Освобождение дискового пространства на production сервере (96% заполнено)
  • Изоляция рисков - проблемы Gitea не влияют на production
  • Улучшение производительности Git операций
  • Масштабируемость

Зеркальный сервер (206.189.35.205)

Для резервирования и высокой доступности настроено зеркалирование всех репозиториев на внешний сервер.

Документация:

Быстрый старт:

# Развертывание зеркального сервера
bash scripts/deployment/gitea/deploy-gitea-mirror-server.sh

# Миграция всех репозиториев
bash scripts/deployment/mirror-all-repos-to-external.sh

Архитектура:

  • Основной сервер (209.38.32.21): Primary Gitea, все репозитории
  • Зеркальный сервер (206.189.35.205): Mirror Gitea, автоматическая синхронизация

🔗 Ссылки


Текущий статус

Репозиторий local_machine

Система (сервер Gitea)

  • Gitea контейнер на 209.38.32.21
  • PostgreSQL, SSL/TLS, Nginx
  • DNS: gitea.hunab.app → 209.38.32.21

Последнее обновление: 2026-03-14
Версия документа: 2.0 (адаптация под проект local_machine)
Изменения:

  • Гайд переориентирован на репозиторий local_machine; примеры и команды приведены к этому проекту
  • Добавлена ссылка на LOCAL_MACHINE_GITEA_SETUP.md для быстрого старта

🧹 Очистка истории Git от build-артефактов

🎯 Когда нужна очистка vs простое удаление

99% случаев: Файлы уже в .gitignore, но Git их отслеживает (закоммичены до добавления в .gitignore)

Решение: Простое удаление из tracking (см. ниже) — БЕЗ переписывания истории!

Пример ниже — для репо с frontend (build-артефакты). В local_machine: убедиться, что путь в .gitignore, затем git rm --cached -r путь/ и коммит.

1% случаев: Нужно удалить файлы из ВСЕЙ истории (например, случайно закоммитили секреты)

Решение: ⚠️ Очистка истории (см. раздел "Когда действительно нужна очистка истории")


ПРАВИЛЬНЫЙ ПОДХОД: Простое удаление из tracking

Когда использовать: Файлы уже в .gitignore, но Git их отслеживает.

Пример проблемы:

  • Файлы в .gitignore: frontend/dist-*
  • Git все еще отслеживает: 4046 файлов из dist-* папок
  • git status показывает: D frontend/dist-cache-busting/404.html и т.д.

Решение (ПРАВИЛЬНО):

# 1. Убедиться что файлы в .gitignore
cat frontend/.gitignore | grep dist-

# 2. Удалить из tracking (файлы останутся в рабочей директории, если есть)
git rm --cached -r frontend/dist-cache-busting/ \
                    frontend/dist-fast-no-image/ \
                    frontend/dist-via-proxy/ \
                    frontend/dist-zero-downtime/ 2>/dev/null || \
git add -u -- frontend/dist-*/  # Альтернатива если файлы уже удалены из рабочей директории

# 3. Проверить что файлы в staging для удаления
git status --short | grep "^D " | wc -l
# Должно показать количество удаляемых файлов

# 4. Закоммитить удаление
git commit -m "chore(frontend): удаление dist-* build-артефактов из Git tracking

- Удалены файлы из frontend/dist-cache-busting/, dist-fast-no-image/, dist-via-proxy/, dist-zero-downtime/
- Файлы были в .gitignore, но отслеживались Git (закоммичены до добавления в .gitignore)
- Build-артефакты генерируются при деплое и не должны храниться в репозитории"

# 5. Push в Gitea (для local_machine: git push origin main или SSH)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev  # в других репо; для local_machine: git push origin main

# 6. Проверка
git ls-files | grep -E "^frontend/dist-(zero-downtime|fast-no-image|via-proxy|cache-busting)/" | wc -l
# Должно быть: 0

Результат:

  • Файлы удалены из tracking (больше не отслеживаются Git)
  • История НЕ переписана (SHA коммитов не изменились)
  • Все коммиты сохранены
  • Работает быстро и безопасно

⚠️ ВАЖНО: После этого файлы останутся в истории старых коммитов, но новые коммиты их не будут содержать. Это нормально для build-артефактов.


КАК НЕ НАДО: git-filter-repo с --invert-paths

КРИТИЧЕСКАЯ ОШИБКА (НЕ ДЕЛАТЬ ТАК!):

# ❌ НЕПРАВИЛЬНО - УНИЧТОЖАЕТ ВСЕ КОММИТЫ!
git filter-repo \
  --path frontend/dist-zero-downtime/ \
  --path frontend/dist-fast-no-image/ \
  --path frontend/dist-via-proxy/ \
  --path frontend/dist-cache-busting/ \
  --invert-paths \
  --force

Что происходит:

  • git-filter-repo с --invert-paths УНИЧТОЖАЕТ ВСЕ КОММИТЫ
  • Все tree objects становятся пустыми (0 файлов в коммитах)
  • Репозиторий полностью сломан
  • Нужно восстанавливать из бэкапа

Почему это происходит:

  • --invert-paths означает "сохранить все, кроме указанных путей"
  • Но если указанные пути были в КАЖДОМ коммите, то все коммиты становятся пустыми
  • Git не может создать коммит без файлов

Реальный пример ошибки (2026-02-27):

# После git-filter-repo:
git ls-tree -r HEAD --name-only | wc -l
# Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!)

git log --oneline -5
# Коммиты есть, но в них НЕТ ФАЙЛОВ

Решение после ошибки:

# Восстановить из бэкапа
rm -rf .git
mv .git.backup-20260227-171445 .git

# Использовать ПРАВИЛЬНЫЙ подход (см. выше)
git rm --cached -r frontend/dist-*/ && git commit -m "..."

⚠️ Когда действительно нужна очистка истории

Редкие случаи, когда нужна очистка истории:

  1. Случайно закоммитили секреты (пароли, API ключи, токены)

    • ⚠️ Критично: секреты в истории = уязвимость безопасности
    • Нужна полная очистка истории
  2. Огромные бинарные файлы (видео, большие изображения, базы данных)

    • ⚠️ Размер репозитория > 2GB из-за истории
    • Нужна очистка для уменьшения размера
  3. Юридические требования (удаление файлов по запросу)

    • ⚠️ Требуется полное удаление из истории
    • Нужна очистка истории

⚠️ ВАЖНО: Для build-артефактов (dist/, build/) очистка истории НЕ НУЖНА - достаточно простого удаления из tracking!


🧹 Очистка истории от файлов с секретами (docs)

Когда применять: после замены реальных секретов на плейсхолдеры в документации (см. SECRETS_AND_FILE_PERMISSIONS_POLICY.md) нужно удалить из истории Git старые версии этих файлов, чтобы секреты нельзя было извлечь из старых коммитов.

Гарантированно безопасный порядок (обязательно):

  1. Бэкап репозитория (ОБЯЗАТЕЛЬНО) — без бэкапа не приступать.
  2. Запуск скрипта, который делает бэкап сам и выполняет очистку по списку путей.
  3. Проверки целостности (в скрипте).
  4. Применение очищенной версии на Gitea только после бэкапа репо на сервере.

Скрипт очистки (бэкап + filter-repo + проверки)

Используется готовый скрипт, который:

  • Создаёт резервную копию (git clone --mirror) в каталог рядом с репо.
  • Клонирует репо в bare, удаляет из истории пути из списка (git filter-repo --paths-from-file ... --invert-paths).
  • Проверяет целостность (git ls-tree -r HEAD | wc -l > 0, git fsck).
  • Восстанавливает в текущем tip почищенные версии файлов (один коммит с плейсхолдерами).

Запуск (из корня репозитория):

# Установить git-filter-repo (один раз)
# macOS:
brew install git-filter-repo
# Linux:
# pip3 install git-filter-repo

# Запуск (бэкап создаётся скриптом автоматически)
bash scripts/maintenance/git-history-cleanup-secrets.sh /path/to/local_machine

Файлы:

  • Список путей (файлы, которые удаляются из истории): scripts/maintenance/git-history-cleanup-secrets-paths.txt — соответствует таблице в SECRETS_AND_FILE_PERMISSIONS_POLICY.md (очистка 2026-03-03).
  • Скрипт: scripts/maintenance/git-history-cleanup-secrets.sh.

После скрипта: выполнить шаги из раздела ниже «Правильная очистка истории»: создание bundle из очищенного bare-репо, загрузка на сервер, бэкап Gitea-репо на сервере, замена objects/refs, перезапуск Gitea, обновление локального репо.

Критично: на production-сервере перед заменой репозитория в Gitea обязательно создать бэкап:

GITEA_REPO="/data/git/repositories/hunabgit/local_machine.git"
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"

Правильная очистка истории (если действительно нужна)

⚠️ КРИТИЧЕСКИ ВАЖНО: Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется.

Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!)

# Создать полную резервную копию репозитория
cd /path/to/repo
git clone --mirror . ../repo-backup-$(date +%Y%m%d-%H%M%S).git

# Проверить размер
du -sh ../repo-backup-*.git

Результат: Резервная копия с полной историей (можно восстановить в любой момент)

Шаг 2: Установка git-filter-repo

# macOS
brew install git-filter-repo

# Linux
pip3 install git-filter-repo

# Проверка
git filter-repo --version

Шаг 3: Удаление файлов из истории (ПРАВИЛЬНЫЙ СПОСОБ)

⚠️ КРИТИЧЕСКИ ВАЖНО: Используйте --path БЕЗ --invert-paths для удаления конкретных файлов!

# Создать bare clone для безопасной работы
cd /tmp
git clone --mirror /path/to/repo repo-clean.git

# ✅ ПРАВИЛЬНО: Удалить конкретные файлы/папки
cd repo-clean.git
git filter-repo \
  --path frontend/dist-zero-downtime/ \
  --path frontend/dist-fast-no-image/ \
  --path frontend/dist-via-proxy/ \
  --path frontend/dist-cache-busting/ \
  --path-glob '*.secret' \
  --path-glob '*.key' \
  --invert-paths \
  --force

# ❌ НЕПРАВИЛЬНО: --invert-paths может уничтожить все коммиты!
# Используйте ТОЛЬКО если уверены что файлы НЕ в каждом коммите

# Очистить reflog и неиспользуемые объекты
git reflog expire --expire=now --all
git gc --prune=now --aggressive

⚠️ КРИТИЧЕСКОЕ ПРЕДУПРЕЖДЕНИЕ:

  • --invert-paths ОПАСЕН - может уничтожить все коммиты если указанные пути были в каждом коммите
  • Лучше использовать: --path для удаления конкретных файлов БЕЗ --invert-paths
  • Проверяйте результат: git ls-tree -r HEAD --name-only | wc -l должно быть > 0!

Шаг 4: Проверка целостности (КРИТИЧЕСКИ ВАЖНО!)

cd repo-clean.git

# ✅ КРИТИЧЕСКАЯ ПРОВЕРКА: Файлы в коммитах
git ls-tree -r HEAD --name-only | wc -l
# ДОЛЖНО БЫТЬ > 0! Если 0 - репозиторий сломан, восстанавливайте из бэкапа!

# Проверить размер
du -sh .
# Должно быть меньше оригинала

# Проверить количество коммитов
git log --oneline --all | wc -l
# Должно быть идентично оригиналу

# Проверить что файлы удалены
git rev-list --all --objects | grep -E "(dist-zero-downtime|dist-fast-no-image)" | wc -l
# Должно быть: 0

# Проверить целостность
git fsck --full
# Не должно быть ошибок

# Проверить важные ветки
git branch -a | grep -E "(dev|main|staging)"
# Все важные ветки должны быть сохранены

Гарантии:

  • Все коммиты сохранены (количество идентично)
  • Файлы в коммитах > 0 (критично!)
  • Все сообщения коммитов идентичны
  • Все авторы и даты сохранены
  • Все важные ветки сохранены
  • Все теги сохранены
  • Файлы удалены из истории

⚠️ КРИТИЧЕСКИ ВАЖНО: Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется.

Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!)

# Создать полную резервную копию репозитория
cd /path/to/repo
git clone --mirror . ../repo-backup-$(date +%Y%m%d-%H%M%S).git

# Проверить размер
du -sh ../repo-backup-*.git

Результат: Резервная копия с полной историей (можно восстановить в любой момент)

Шаг 2: Установка git-filter-repo

# macOS
brew install git-filter-repo

# Linux
pip3 install git-filter-repo

# Проверка
git filter-repo --version

Шаг 3: Удаление файлов из истории

# Создать bare clone для безопасной работы
cd /tmp
git clone --mirror /path/to/repo repo-clean.git

# Удалить папки из истории
cd repo-clean.git
git filter-repo \
  --path frontend/dist-zero-downtime/ \
  --path frontend/dist-fast-no-image/ \
  --path frontend/dist-via-proxy/ \
  --path frontend/dist-cache-busting/ \
  --invert-paths \
  --force

# Очистить reflog и неиспользуемые объекты
git reflog expire --expire=now --all
git gc --prune=now --aggressive

Параметры:

  • --path - путь к папке/файлу для удаления
  • --path-glob - glob паттерн для удаления (например, *.secret)
  • --invert-paths - ⚠️ ОПАСНО! Удалить указанные пути (остальное сохранить). Может уничтожить все коммиты!
  • --force - принудительное выполнение

Результат: Очищенный репозиторий без указанных файлов

⚠️ КРИТИЧЕСКОЕ ПРЕДУПРЕЖДЕНИЕ:

  • --invert-paths ОПАСЕН - если указанные пути были в КАЖДОМ коммите, все коммиты станут пустыми!
  • Всегда проверяйте: git ls-tree -r HEAD --name-only | wc -l должно быть > 0 после очистки!
  • Если результат = 0: Репозиторий сломан, восстанавливайте из бэкапа!

Шаг 4: Проверка целостности (ОБЯЗАТЕЛЬНО!)

cd repo-clean.git

# Проверить размер
du -sh .
# Должно быть меньше оригинала

# Проверить количество коммитов
git log --oneline --all | wc -l
# Должно быть идентично оригиналу

# Проверить что файлы удалены
git rev-list --all --objects | grep -E "(dist-zero-downtime|dist-fast-no-image)" | wc -l
# Должно быть: 0

# Проверить целостность
git fsck --full
# Не должно быть ошибок

# Проверить важные ветки
git branch -a | grep -E "(dev|main|staging)"
# Все важные ветки должны быть сохранены

Гарантии:

  • Все коммиты сохранены (количество идентично)
  • Все сообщения коммитов идентичны
  • Все авторы и даты сохранены
  • Все важные ветки сохранены
  • Все теги сохранены
  • Файлы удалены из истории

Шаг 5: Применение очищенной версии

⚠️ ПРОБЛЕМА: Прямой push в Gitea таймаутит из РФ, а скрипт через прокси не работает, потому что после замены .git нет remote tracking branches.

РЕШЕНИЕ: Bundle метод + прямое копирование на сервер

# 1. Создать bundle из очищенного репозитория
cd /tmp/repo-clean.git
git bundle create /tmp/repo-clean.bundle --all

# 2. Загрузить bundle на production сервер
scp -i ~/.ssh/hunab_deploy_key /tmp/repo-clean.bundle hunab@209.38.32.21:/tmp/

# 3. Создать временный репозиторий из bundle на сервере
ssh hunab-prod "cd /tmp && git clone --mirror /tmp/repo-clean.bundle repo-clean.git"

# 4. Бэкап текущего Gitea репозитория
GITEA_REPO="/data/git/repositories/hunabgit/local_machine.git"
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"

# 5. Копировать очищенный репозиторий в контейнер
ssh hunab-prod "docker cp /tmp/repo-clean.git gitea:/tmp/repo-clean.git"

# 6. Заменить objects и refs в Gitea репозитории
ssh hunab-prod "docker exec gitea sh -c '
  cd $GITEA_REPO
  rm -rf objects packed-refs refs/heads refs/tags
  cp -r /tmp/repo-clean.git/objects .
  cp -r /tmp/repo-clean.git/packed-refs . 2>/dev/null || true
  cp -r /tmp/repo-clean.git/refs/heads refs/ 2>/dev/null || true
  cp -r /tmp/repo-clean.git/refs/tags refs/ 2>/dev/null || true
  echo \"ref: refs/heads/main\" > HEAD
  rm -rf refs/original filter-repo
  git reflog expire --expire=now --all 2>/dev/null
  git gc --prune=now 2>&1 | tail -5
  chown -R git:git .
'"

# 7. Перезапустить Gitea для обновления индексов
ssh hunab-prod "docker restart gitea && sleep 5"

# 8. Проверка
ssh hunab-prod "docker exec gitea sh -c 'cd $GITEA_REPO && \
  echo \"Размер: \$(du -sh .)\" && \
  echo \"Коммитов: \$(git log --oneline --all | wc -l)\" && \
  echo \"Файлов dist в истории: \$(git rev-list --all --objects | grep -cE \"dist-zero-downtime|dist-fast-no-image|dist-via-proxy|dist-cache-busting\" || echo 0)\" && \
  echo \"Веток: \$(git branch | wc -l)\" && \
  echo \"Тегов: \$(git tag | wc -l)\" && \
  git log --oneline -3 main'"

Результат: Gitea репозиторий обновлен очищенной версией

Шаг 6: Обновление локального репозитория

# Заменить .git папку очищенной версией
cd /path/to/repo
rm -rf .git
cp -r /tmp/repo-clean.git .git

# Преобразовать bare в обычный репозиторий
git config core.bare false
git config core.worktree .

# Проверить статус
git status
git log --oneline -5

⚠️ ВАЖНО: После замены .git могут появиться артефакты в git status (untracked файлы из .git). Это не критично - репозиторий работает корректно.

📊 Результаты: Простое удаление из tracking (ПРАВИЛЬНЫЙ подход)

До удаления:

  • Файлов отслеживается: 13,281
  • Файлов dist-* в tracking: 4,046
  • Коммитов: 6,218

После удаления:

  • Файлов отслеживается: 13,281 - 4,046 = 9,235
  • Файлов dist-* в tracking: 0
  • Коммитов: 6,218 (все сохранены, SHA не изменились)
  • Веток: 27 (все важные сохранены)
  • Тегов: 162 (все сохранены)
  • Время выполнения: ~30 секунд
  • Риск: Минимальный (простой коммит)

⚠️ ВАЖНО: Файлы останутся в истории старых коммитов, но новые коммиты их не будут содержать. Это нормально для build-артефактов.

📊 Результаты: Очистка истории (если действительно нужна)

До очистки:

  • Размер: 968 MB
  • Файлов в истории: 4,046 из dist-* папок
  • Коммитов: 6,218

После очистки (если все прошло правильно):

  • Размер: 391 MB (экономия 577 MB, 60%)
  • Файлов в истории: 0 из dist-* папок
  • Коммитов: 6,218 (все сохранены, но SHA изменились)
  • Веток: 27 (все важные сохранены)
  • Тегов: 162 (все сохранены)
  • Время выполнения: ~30-60 минут (с бэкапом и проверками)
  • Риск: Высокий (переписывание истории) ⚠️

Результаты после ОШИБКИ (git-filter-repo с --invert-paths):

  • Файлов в коммитах: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!)
  • Репозиторий: СЛОМАН
  • Решение: Восстановление из бэкапа

🛡️ Механизмы защиты

Для простого удаления из tracking:

  1. Проверка .gitignore: Убедиться что файлы в .gitignore
  2. Проверка staging: git status --short | grep "^D " показывает удаляемые файлы
  3. Проверка после коммита: git ls-files | grep dist- | wc -l = 0

Для очистки истории (если действительно нужна):

  1. Резервная копия: Полная копия оригинального репозитория (ОБЯЗАТЕЛЬНО!)
  2. Бэкап на сервере: Копия Gitea репозитория перед заменой
  3. КРИТИЧЕСКАЯ проверка: git ls-tree -r HEAD --name-only | wc -l > 0 после очистки!
  4. Проверка целостности: git fsck --full перед применением
  5. Валидация: Проверка количества коммитов, веток, тегов, файлов в коммитах

⚠️ Критические ошибки и решения

Ошибка 0: git-filter-repo уничтожил все коммиты (КРИТИЧЕСКАЯ!)

Проблема: После git-filter-repo --invert-paths все коммиты стали пустыми (0 файлов).

Симптомы:

git ls-tree -r HEAD --name-only | wc -l
# Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!)

git log --oneline -5
# Коммиты есть, но в них НЕТ ФАЙЛОВ

git status
# Показывает ВСЕ файлы как untracked

Причина: --invert-paths удалил указанные пути из ВСЕХ коммитов. Если эти пути были в каждом коммите, все коммиты стали пустыми.

Решение:

# 1. Восстановить из бэкапа
rm -rf .git
mv .git.backup-20260227-171445 .git  # Используйте ваш бэкап

# 2. Использовать ПРАВИЛЬНЫЙ подход (простое удаление из tracking)
git rm --cached -r frontend/dist-*/ && git commit -m "..."

# 3. НЕ использовать git-filter-repo для build-артефактов!

Профилактика:

  • ВСЕГДА проверяйте git ls-tree -r HEAD --name-only | wc -l после git-filter-repo
  • ВСЕГДА создавайте полный бэкап перед очисткой истории
  • НЕ используйте --invert-paths для файлов, которые были в каждом коммите
  • Для build-артефактов используйте простое удаление из tracking (см. "ПРАВИЛЬНЫЙ ПОДХОД" выше)

Ошибка 1: Прямой push таймаутит

Проблема: git push --force зависает с таймаутом из РФ.

Решение: Использовать bundle метод + прямое копирование на сервер (см. Шаг 5).

Ошибка 2: Скрипт через прокси не работает

Проблема: После замены .git нет remote tracking branches (origin/dev не существует).

Решение: Bundle метод обходит эту проблему, так как работает напрямую с файловой системой.

Ошибка 3: Файлы из .git в корне проекта

Проблема: После замены .git git status показывает untracked файлы (COMMIT_EDITMSG, HEAD, config и т.д.).

Решение: Это артефакты git status - файлы не существуют в корне, можно игнорировать. Репозиторий работает корректно.

Ошибка 4: Gitea не видит изменения

Проблема: После замены объектов Gitea не обновляет индекс.

Решение:

  1. Удалить refs/original и filter-repo из репозитория
  2. Очистить reflog: git reflog expire --expire=now --all
  3. Перезапустить Gitea: docker restart gitea

🔄 Восстановление из резервной копии

Если что-то пошло не так:

# Восстановить локальный репозиторий
cd /path/to/repo
rm -rf .git
git clone --mirror /path/to/backup/repo-backup-*.git .git
git config core.bare false
git config core.worktree .

# Восстановить Gitea репозиторий на сервере
ssh hunab-prod "docker exec gitea rm -rf $GITEA_REPO && \
  docker exec gitea cp -r ${GITEA_REPO}.backup-* $GITEA_REPO && \
  docker exec gitea chown -R git:git $GITEA_REPO && \
  docker restart gitea"

📋 Чеклист: Простое удаление из tracking (99% случаев)

Для build-артефактов (dist/, build/) - используйте этот чеклист:

  • Проверено что файлы в .gitignore
  • Удалены файлы из tracking: git rm --cached -r frontend/dist-*/
  • Проверено что файлы в staging для удаления: git status --short | grep "^D "
  • Создан коммит с описанием удаления
  • Push в Gitea (local_machine: git push origin main или SSH)
  • Проверено что файлы больше не отслеживаются: git ls-files | grep dist- | wc -l = 0
  • Обновлен CHANGELOG с описанием операции

Результат: Файлы удалены из tracking, история НЕ переписана, все работает быстро и безопасно.


📋 Чеклист: Очистка истории (1% случаев - только для секретов/юридических требований)

⚠️ ВАЖНО: Используйте ТОЛЬКО если действительно нужно удалить файлы из ВСЕЙ истории!

  • Определено что простая очистка tracking НЕДОСТАТОЧНА (секреты, юридические требования)
  • Создана резервная копия оригинального репозитория
  • Установлен git-filter-repo
  • Создан bare clone для безопасной работы
  • Удалены файлы из истории через git-filter-repo (БЕЗ --invert-paths если возможно!)
  • КРИТИЧЕСКАЯ ПРОВЕРКА: git ls-tree -r HEAD --name-only | wc -l > 0 (если 0 - репозиторий сломан!)
  • Очищены reflog и неиспользуемые объекты
  • Проверена целостность (коммиты, ветки, теги, файлы в коммитах > 0)
  • Создан bundle из очищенного репозитория
  • Загружен bundle на production сервер (в /home/hunab/, НЕ в /tmp/ - tmpfs только 2GB!)
  • Создан бэкап Gitea репозитория на сервере
  • Заменены objects и refs в Gitea репозитории
  • Удалены refs/original и filter-repo
  • Перезапущен Gitea
  • Проверен результат (размер, коммиты, файлы в коммитах > 0)
  • Обновлен локальный репозиторий
  • Обновлен CHANGELOG с описанием операции

📚 Связанные документы


Последнее обновление: 2026-02-27
Версия документа: 1.6
Статус: Gitea полностью развернут и работает стабильно
Изменения:

  • Добавлен резервный способ коммитов через временный скрипт (2026-02-16)
  • Добавлен гайд по очистке истории Git от build-артефактов (2026-02-27)
  • КРИТИЧЕСКОЕ ОБНОВЛЕНИЕ (2026-02-27): Добавлены четкие предупреждения о том, как НЕ надо делать очистку истории:
    • git-filter-repo --invert-paths УНИЧТОЖАЕТ все коммиты (реальный пример ошибки 2026-02-27)
    • Правильный подход: простое удаление из tracking (git rm --cached) для build-артефактов (99% случаев)
    • Очистка истории нужна ТОЛЬКО для секретов/юридических требований (1% случаев)
    • Добавлены критические проверки (git ls-tree -r HEAD --name-only | wc -l > 0) и механизмы защиты
  • ОПТИМИЗАЦИЯ (2026-02-27): Исправлена медленная верификация push - заменен git fetch origin на git update-ref для мгновенного обновления tracking ref без сетевых запросов