Files
local_machine/docs/git/GITEA_COMPLETE_GUIDE.md
2026-03-14 20:03:57 +03:00

1864 lines
85 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Gitea Complete Guide - Hunab.app
> **Для репозитория legal на сервере 206.189.35.205** см. **[GITEA_LEGAL_206_SERVER_GUIDE.md](./GITEA_LEGAL_206_SERVER_GUIDE.md)** (единый стандарт: push, пути, мониторинг).
> Ниже — гайд по Gitea для Hunab.app (основной сервер 209.38.32.21).
---
**Дата:** 2026-01-02
**Версия Gitea:** 1.25.3
**Статус:** ✅ Полностью развернут и работает
**Последнее обновление:** Резервный способ коммитов через временный скрипт (2026-02-16)
---
## 📋 Обзор
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 не используются.
---
## 🚀 Развертывание
### Быстрый старт
```bash
# Развертывание 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)
```yaml
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):**
```ini
[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):**
```ini
[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.
**Решение:**
#### Автоматический скрипт (рекомендуется)
```bash
# На сервере 206.189.35.205
bash scripts/deployment/fix-postgres-utils-user.sh
```
#### Ручное создание пользователя
```bash
# Определение суперпользователя 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"** - Пользователь не создан:
```bash
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "CREATE USER utils WITH PASSWORD 'YOUR_PASSWORD';"
```
2. **"password authentication failed"** - Пароль неверный:
```bash
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "ALTER USER utils WITH PASSWORD 'YOUR_PASSWORD';"
```
3. **"permission denied for database"** - Нет прав:
```bash
docker exec gitea-mirror-postgres psql -U postgres -d postgres -c "GRANT ALL PRIVILEGES ON DATABASE hunabgit TO utils;"
```
**Проверка после создания:**
```bash
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
```bash
# Получение сертификата
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`:
```nginx
# 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 репозиториев (КРИТИЧЕСКИ ВАЖНО)
### ❌ Проблема: 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, но соединение обрывается во время передачи данных.
### ✅ Систематическое решение: Универсальный скрипт
**РЕКОМЕНДУЕТСЯ:** Используйте универсальный скрипт, который автоматически выбирает лучший метод:
```bash
# Обычный скрипт (для работы не из РФ)
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
# 🇷🇺 ДЛЯ РФ: Скрипт через прокси (ускоряет в 5-10 раз)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]
```
**⚠️ КРИТИЧЕСКИ ВАЖНО:** При работе из Российской Федерации **ПРЕДПОЧТИТЕЛЬНО** использовать скрипт с суффиксом `-via-proxy`!
**🇷🇺 ПРАВИЛО ДЛЯ РФ:**
- ✅ **ВСЕГДА используйте прокси-версии** для push и создания тегов из РФ
- ✅ **Ускорение в 5-10 раз** по сравнению с прямым подключением
- ✅ **Стабильное соединение** через российский прокси-сервер (149.154.64.19)
- ✅ **Избежание таймаутов** - прямое подключение из РФ часто нестабильно
**Скрипт автоматически:**
1. Пытается SSH push (если доступен, через прокси для `-via-proxy`)
2. Пытается HTTPS push с оптимизированными настройками (только на Linux с `timeout`)
3. Использует bundle метод (всегда работает, особенно на macOS)
4. ✅ **Мгновенная верификация** - обновляет локальный tracking ref напрямую через `git update-ref` (без медленного `git fetch`)
**Пример:**
```bash
# Push текущей ветки (по умолчанию dev)
bash scripts/deployment/gitea/git-push-gitea.sh
# 🇷🇺 ДЛЯ РФ: Push через прокси (рекомендуется из РФ)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh
# Push конкретной ветки
bash scripts/deployment/gitea/git-push-gitea.sh main
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh main # 🇷🇺 ДЛЯ РФ
# Режим проверки без пуша (ничего не меняет, только показывает сколько коммитов уйдёт)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh --dry-run dev
```
**Безопасность скрипта пуша:**
- Не трогает текущую ветку, HEAD, рабочую копию и локальные коммиты.
- Не выполняет: `git reset`, `git checkout --hard`, `git clean`, `git branch -D`, `push --force`.
- Единственная запись в репо: обновление `refs/remotes/origin/<branch>` только после успешного пуша и только на кончик текущей ветки (все «ждущие пуша» коммиты при этом уже на сервере).
- Перед пушем можно проверить: `--dry-run` — показывает число коммитов к пушу, ничего не отправляет и не меняет.
**Git Alias (для удобства):**
Добавьте в `~/.gitconfig`:
```ini
[alias]
gpush = !bash -c 'cd "$(git rev-parse --show-toplevel)" && bash scripts/deployment/gitea/git-push-gitea.sh "$@"' -
```
Использование: `git gpush dev`
### ✅ Резервный способ: Временный скрипт для коммита и пуша
**Когда использовать:**
- Терминал недоступен из-за проблем с zsh/shell
- Агент не может выполнить команды напрямую
- Нужно подготовить все команды заранее
**Процесс:**
1. **Агент создает временный скрипт** в `temp/commit-and-push.sh` с полным коммитом и push командой
2. **Пользователь выполняет скрипт** вручную в терминале
3. **Скрипт автоматически** делает `git add`, `git commit` и `git push` через универсальный скрипт
**Формат временного скрипта:**
```bash
#!/bin/bash
# Скрипт для коммита и пуша изменений
set -e
echo "📦 Committing changes..."
git add -A
echo "💾 Creating commit..."
git commit -m "🎨 feat(frontend): центрирование нод при tidyUp с учетом реальных ширин v3.10.4
- ✅ Улучшена функция calculateLevelPositions для центрирования нод
- ✅ Добавлен учет реальных ширин нод (node.width или NODE_WIDTH)
- ✅ Вся группа нод на уровне центрируется относительно START_X
- ✅ Более широкие ноды правильно центрированы относительно узких
- ✅ Обновлена документация (TROUBLESHOOTING_COMPLETE.md, CHANGELOG.md)
Проблема: Более широкие ноды выравнивались по левому краю
Решение: Центрирование с учетом реальных ширин нод
Результат: Ноды центрируются на уровне, визуально выглядит красиво"
echo "🚀 Pushing to Gitea..."
bash scripts/deployment/gitea/git-push-gitea.sh dev
echo "✅ Done!"
```
**Требования к скрипту:**
- ✅ Должен быть исполняемым (`chmod +x`)
- ✅ Должен содержать полное сообщение коммита с описанием изменений
- ✅ Должен использовать универсальный скрипт `git-push-gitea.sh` для push
- ✅ Должен быть в папке `temp/` для временных скриптов
- ✅ Должен иметь понятные echo сообщения для пользователя
**Использование:**
```bash
# Выполнить скрипт
bash temp/commit-and-push.sh
```
**Очистка:**
После успешного выполнения скрипт можно удалить:
```bash
rm temp/commit-and-push.sh
```
**⚠️ ВАЖНО:**
- Скрипт должен быть создан агентом **ПЕРЕД** запросом пользователя на коммит
- Агент должен **ВСЕГДА** готовить скрипт, если терминал недоступен
- Скрипт должен содержать **ПОЛНОЕ** описание изменений в коммите
### Стандарт: без трейлеров в коммитах
В проекте **не используются** git-трейлеры в сообщениях коммитов (строки вида `Key: Value` в конце сообщения). Не добавлять:
- `Made-with: Cursor` и аналогичные метки инструмента
- `Co-authored-by:`, `Signed-off-by:` и т.п., если это не требуется явно политикой репозитория
Коммиты должны соответствовать [COMMIT_STANDARDS.md](../../standards/COMMIT_STANDARDS.md) и содержать только заголовок и при необходимости тело сообщения в стандартном формате. См. также раздел «Commit Quality Standards» в `.cursorrules`.
### Неблокирующая проверка типов (при коммитах)
Перед коммитом или пушем можно быстро проверить типы (frontend и/или backend) **без блокировки коммита** — скрипт всегда выходит с кодом 0 и только выводит ошибки в консоль.
```bash
# Frontend + backend (полный отчёт)
bash scripts/maintenance/typecheck-report.sh
# Только изменённая часть (быстрее)
bash scripts/maintenance/typecheck-report.sh frontend
bash scripts/maintenance/typecheck-report.sh backend
```
Отдельно в пакетах:
- `cd frontend && pnpm typecheck` — только frontend
- `cd backend && pnpm typecheck` — только backend
**Рекомендация:** запускать при коммитах бэкенда/фронта или перед пушем; коммит не блокируется, но ошибки видны в выводе.
### Проверка прав на .env после деплоя (SECRETS_AND_FILE_PERMISSIONS_POLICY.md §4)
После деплоя бэкенда на production выполнить проверку прав на `.env.production` (и при необходимости `.env.staging`) на сервере. Встроена в скрипты `deploy-backend-core-only.sh` и `deploy-backend-core-only-via-proxy.sh`; при необходимости запустить вручную:
```bash
# После деплоя backend на прод (или вручную)
bash scripts/deployment/verify-env-permissions.sh hunab-prod
# либо с явным хостом
bash scripts/deployment/verify-env-permissions.sh hunab@209.38.32.21
```
Ожидается: права `600` и владелец `hunab:hunab` для всех `.env*` в `/opt/app/`. При несоответствии скрипт выводит предупреждение и команду для исправления; деплой не блокируется (exit 0).
**Чеклист коммитов/деплоя:** после деплоя backend на production — выполнить проверку прав (или убедиться, что она уже выполнена скриптом деплоя). См. [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md) §4.
### Метод 1: HTTPS с оптимизированными настройками
```bash
# Настройка git для больших push
git config http.postBuffer 524288000
git config http.lowSpeedLimit 0
git config http.lowSpeedTime 0
git config http.timeout 600
# Попытка push
git push origin dev
```
**⚠️ На macOS:** HTTPS push часто не работает из-за отсутствия команды `timeout`. Скрипт автоматически пропускает HTTPS и использует bundle метод.
### Метод 2: Bundle метод (надежный fallback)
Если HTTPS не работает, используется прямой доступ через файловую систему:
```bash
# Создать bundle
git bundle create /tmp/push.bundle origin/dev..dev
# Скопировать на сервер
scp /tmp/push.bundle hunab-prod:/tmp/
# Применить в Gitea
ssh hunab-prod "docker cp /tmp/push.bundle gitea:/tmp/push.bundle && \
docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && \
git bundle unbundle /tmp/push.bundle && \
git update-ref refs/heads/dev \$(git bundle list-heads /tmp/push.bundle | grep dev | cut -d\" \" -f1) && \
rm /tmp/push.bundle' && rm /tmp/push.bundle"
```
**✅ Статус:** Bundle метод всегда работает и обходит все проблемы с таймаутами.
### Сравнение методов
| Метод | Скорость | Надежность | Сложность | Платформа |
|-------|----------|------------|-----------|-----------|
| **SSH** | ⚡⚡⚡ Быстро | ✅✅✅ Высокая | ✅ Просто | Все |
| **HTTPS (оптимизированный)** | ⚡⚡ Средне | ✅✅ Средняя | ✅ Просто | Linux |
| **Bundle (прямой доступ)** | ⚡ Медленно | ✅✅✅ Всегда работает | ⚠️ Сложнее | Все |
**Рекомендация:** Используйте автоматический скрипт - он выберет лучший метод автоматически.
### Миграция репозиториев
**Для регулярных push используйте скрипт:**
```bash
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
```
#### Шаг 1: Создание bundle из локального репозитория
```bash
cd /Users/eternal/code/hunabapp-dev
git bundle create /tmp/hunabapp-migration.bundle --all
```
**Результат:** Bundle ~553M
#### Шаг 2: Копирование bundle на сервер
```bash
scp /tmp/hunabapp-migration.bundle hunab-prod:/tmp/hunabapp-migration.bundle
```
#### Шаг 3: Распаковка bundle на сервере
```bash
ssh hunab-prod "cd /tmp && git clone --mirror hunabapp-migration.bundle hunabapp-mirror.git"
```
**Результат:** Bare репозиторий ~557M
#### Шаг 4: Копирование в директорию Gitea
```bash
ssh hunab-prod "
docker exec gitea mkdir -p /data/git/repositories/hunabgit/
docker cp /tmp/hunabapp-mirror.git gitea:/data/git/repositories/hunabgit/hunabapp.git
docker exec gitea chown -R git:git /data/git/repositories/hunabgit/hunabapp.git
"
```
**Результат:** Репозиторий в Gitea (556.6M)
#### Шаг 5: Распаковка refs из packed-refs
**КРИТИЧЕСКИ ВАЖНО:** Gitea не видит ветки в `packed-refs`, нужно распаковать:
```bash
ssh hunab-prod "docker exec gitea sh -c 'cd /data/git/repositories/hunabgit/hunabapp.git && cat packed-refs | grep \"refs/heads/\" | while read sha ref; do mkdir -p \$(dirname \$ref) && echo \$sha > \$ref; done'"
```
#### Шаг 6: Обновление базы данных
```bash
ssh hunab-prod "
docker exec hunab-prod-postgres psql -U hunabgit -d hunabgit -c \"UPDATE repository SET is_empty = false WHERE name = 'hunabapp';\"
docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini
docker restart gitea
"
```
#### Шаг 7: Проверка
```bash
# Проверка веток через API
curl -s -H "Authorization: token YOUR_TOKEN" \
https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/branches | jq '.[] | .name'
# Проверка файлов через API
curl -s -H "Authorization: token YOUR_TOKEN" \
https://gitea.hunab.app/api/v1/repos/hunabgit/hunabapp/git/trees/dev?recursive=0 | jq '.tree[] | .path'
# Проверка через веб-интерфейс
https://gitea.hunab.app/hunabgit/hunabapp
```
**✅ Результат:** Репозиторий полностью мигрирован, все файлы доступны через веб-интерфейс и 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
```bash
# В локальном репозитории
cd /Users/eternal/code/hunabapp-dev
git remote set-url origin https://TOKEN@gitea.hunab.app/hunabgit/hunabapp.git
```
---
## 🏷️ Создание тегов и Release (СТАНДАРТ)
### ⚠️ КРИТИЧЕСКИ ВАЖНО: Gitea не обновляет индекс тегов автоматически
**Проблема:** Если создать тег напрямую в git репозитории (`git tag -a v1.0.0`), Gitea **НЕ покажет его в веб-интерфейсе** автоматически. Gitea кэширует список тегов и обновляет его только при создании Release через веб-интерфейс или API.
### ✅ СТАНДАРТ: Создание тега через базу данных (РЕКОМЕНДУЕТСЯ)
**Используйте этот метод для автоматического создания тегов:**
```bash
# 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 = 'hunabapp';\" | 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 = 'hunabapp';`)
- `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/hunabapp/releases/new
2. В поле "Tag" введите имя тега (например, `v1.3.7-CRM`)
3. В поле "Target" выберите commit из списка или введите SHA
4. Заполните Title и Description
5. Нажмите "Publish Release"
**⚠️ Недостаток:** Требует ручного действия, не автоматизируется.
### ✅ Альтернатива: Создание через API (требует токен)
```bash
# 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/hunabapp/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
```bash
# ❌ НЕ РАБОТАЕТ - 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/hunabapp/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
# Обычный скрипт
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
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
- Проверяет существование репозитория
- Выводит понятные сообщения об ошибках
---
## 📊 Мониторинг
### Проверка статуса
```bash
# Контейнер
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'
```
### Проверка репозитория
```bash
# Ветки
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a | wc -l"
# Размер
ssh hunab-prod "docker exec gitea du -sh /data/git/repositories/hunabgit/hunabapp.git"
# Последний коммит
ssh hunab-prod "docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git log -1 --pretty=format:'%H %s'"
```
---
## 🔧 Troubleshooting
Для детальной диагностики проблем см. **[GITEA_TROUBLESHOOTING.md](./GITEA_TROUBLESHOOTING.md)** - Полное руководство по решению проблем.
Для диагностики зеркального сервера см. **[GITEA_MIRROR_TROUBLESHOOTING.md](./GITEA_MIRROR_TROUBLESHOOTING.md)**.
### Gitea не запускается
```bash
# Проверка логов
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 для всех репозиториев:
```bash
ssh hunab-prod "docker exec gitea /usr/local/bin/gitea admin regenerate hooks --config /data/gitea/conf/app.ini"
```
**Проверка:**
```bash
# Проверка прав на hooks
ssh hunab-prod "docker exec gitea ls -la /data/git/repositories/hunabgit/hunabapp.git/hooks/pre-receive"
# Проверка содержимого hooks
ssh hunab-prod "docker exec gitea cat /data/git/repositories/hunabgit/hunabapp.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 сертификат не обновляется
```bash
# Проверка автообновления
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. ✅ Регулярные бэкапы данных
### Бэкапы
```bash
# Полный бэкап 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. ✅ **Репозиторий hunabapp мигрирован** (556.8M, 23 ветки, все файлы доступны)
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
---
## 📚 Полезные команды
```bash
# Push репозитория (РЕКОМЕНДУЕТСЯ)
bash scripts/deployment/gitea/git-push-gitea.sh [branch]
# 🇷🇺 ДЛЯ РФ: Push через прокси (ускоряет в 5-10 раз)
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh [branch]
# Gitea Admin Commands
docker exec gitea /usr/local/bin/gitea admin --help
docker exec gitea /usr/local/bin/gitea admin regenerate hooks
# Git операции в Gitea
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.git branch -a
docker exec gitea git --git-dir=/data/git/repositories/hunabgit/hunabapp.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_MIRRORING_GUIDE.md)**.
### Миграция Gitea на зеркальный сервер
**📋 План миграции:** См. **[GITEA_MIGRATION_TO_MIRROR_SERVER.md](./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)
Для резервирования и высокой доступности настроено зеркалирование всех репозиториев на внешний сервер.
**Документация:**
- [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md) - Полный план зеркалирования
- [Gitea Migration to Mirror Server](./GITEA_MIGRATION_TO_MIRROR_SERVER.md) - **План миграции Gitea на зеркальный сервер (206.189.35.205)**
- [Gitea Mirror Troubleshooting](./GITEA_MIRROR_TROUBLESHOOTING.md) - Диагностика зеркального сервера
- [Gitea Legal — сервер 206.189.35.205](./GITEA_LEGAL_206_SERVER_GUIDE.md) - Стандарт для репозитория legal
**Быстрый старт:**
```bash
# Развертывание зеркального сервера
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, автоматическая синхронизация
---
## 🔗 Ссылки
- **Веб-интерфейс:** https://gitea.hunab.app
- **API:** https://gitea.hunab.app/api/v1/
- **Репозиторий:** https://gitea.hunab.app/hunabgit/hunabapp
- **Документация Gitea:** https://docs.gitea.io/
- **SSL Report:** https://www.ssllabs.com/ssltest/analyze.html?d=gitea.hunab.app
- **Зеркалирование:** [Gitea Mirroring Guide](./GITEA_MIRRORING_GUIDE.md)
- **Миграция на зеркальный сервер:** [Gitea Migration Plan](./GITEA_MIGRATION_TO_MIRROR_SERVER.md)
- **Индекс документации:** [Gitea Index](./INDEX.md) - Перелинкованный индекс всех документов Gitea
---
## ✅ Текущий статус (2025-12-27)
### Репозиторий hunabapp
- ✅ **Размер:** 556.8M
- ✅ **Ветки:** 23 (dev, main, и другие)
- ✅ **Файлы:** Все файлы доступны через веб-интерфейс и API
- ✅ **Веб-интерфейс:** Полностью функционален
- ✅ **API:** Работает корректно
- ✅ **Git операции:** Клонирование, push, pull работают
### Система
- ✅ Gitea контейнер работает стабильно
- ✅ PostgreSQL подключение стабильно
- ✅ SSL/TLS работает корректно
- ✅ Nginx reverse proxy настроен правильно
- ✅ DNS настроен (gitea.hunab.app → 209.38.32.21)
**Последнее обновление:** 2026-02-27
**Версия документа:** 1.4
**Статус:** ✅ Gitea полностью развернут и работает стабильно
**Изменения:**
- Добавлен резервный способ коммитов через временный скрипт (2026-02-16)
- Добавлен гайд по очистке истории Git от build-артефактов (2026-02-27)
---
## 🧹 Очистка истории Git от build-артефактов
### 🎯 Когда нужна очистка vs простое удаление
**99% случаев:** Файлы уже в `.gitignore`, но Git их отслеживает (закоммичены до добавления в `.gitignore`)
**Решение:** ✅ **Простое удаление из tracking** (см. ниже) - БЕЗ переписывания истории!
**1% случаев:** Нужно удалить файлы из ВСЕЙ истории (например, случайно закоммитили секреты)
**Решение:** ⚠️ **Очистка истории** (см. раздел "Когда действительно нужна очистка истории")
---
## ✅ ПРАВИЛЬНЫЙ ПОДХОД: Простое удаление из tracking
**Когда использовать:** Файлы уже в `.gitignore`, но Git их отслеживает.
**Пример проблемы:**
- Файлы в `.gitignore`: `frontend/dist-*`
- Git все еще отслеживает: 4046 файлов из `dist-*` папок
- `git status` показывает: `D frontend/dist-cache-busting/404.html` и т.д.
### ✅ Решение (ПРАВИЛЬНО):
```bash
# 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
bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev # 🇷🇺 ДЛЯ РФ
# 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
### ❌ КРИТИЧЕСКАЯ ОШИБКА (НЕ ДЕЛАТЬ ТАК!):
```bash
# ❌ НЕПРАВИЛЬНО - УНИЧТОЖАЕТ ВСЕ КОММИТЫ!
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
# Коммиты есть, но в них НЕТ ФАЙЛОВ
```
**✅ Решение после ошибки:**
```bash
# Восстановить из бэкапа
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](../../security/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 почищенные версии файлов (один коммит с плейсхолдерами).
**Запуск (из корня репозитория):**
```bash
# Установить 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/hunabapp-dev
```
**Файлы:**
- Список путей (файлы, которые удаляются из истории): `scripts/maintenance/git-history-cleanup-secrets-paths.txt` — соответствует таблице в [SECRETS_AND_FILE_PERMISSIONS_POLICY.md](../../security/SECRETS_AND_FILE_PERMISSIONS_POLICY.md) (очистка 2026-03-03).
- Скрипт: `scripts/maintenance/git-history-cleanup-secrets.sh`.
**После скрипта:** выполнить шаги из раздела ниже «Правильная очистка истории»: создание bundle из очищенного bare-репо, загрузка на сервер, **бэкап Gitea-репо на сервере**, замена objects/refs, перезапуск Gitea, обновление локального репо.
**Критично:** на production-сервере перед заменой репозитория в Gitea обязательно создать бэкап:
```bash
GITEA_REPO="/data/git/repositories/hunabgit/hunabapp.git"
ssh hunab-prod "docker exec gitea cp -r $GITEA_REPO ${GITEA_REPO}.backup-$(date +%Y%m%d)"
```
---
## ✅ Правильная очистка истории (если действительно нужна)
**⚠️ КРИТИЧЕСКИ ВАЖНО:** Очистка истории переписывает все коммиты (SHA изменятся), но содержимое сохраняется.
### Шаг 1: Резервная копия (ОБЯЗАТЕЛЬНО!)
```bash
# Создать полную резервную копию репозитория
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
```bash
# macOS
brew install git-filter-repo
# Linux
pip3 install git-filter-repo
# Проверка
git filter-repo --version
```
### Шаг 3: Удаление файлов из истории (ПРАВИЛЬНЫЙ СПОСОБ)
**⚠️ КРИТИЧЕСКИ ВАЖНО:** Используйте `--path` БЕЗ `--invert-paths` для удаления конкретных файлов!
```bash
# Создать 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: Проверка целостности (КРИТИЧЕСКИ ВАЖНО!)
```bash
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: Резервная копия (ОБЯЗАТЕЛЬНО!)
```bash
# Создать полную резервную копию репозитория
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
```bash
# macOS
brew install git-filter-repo
# Linux
pip3 install git-filter-repo
# Проверка
git filter-repo --version
```
#### Шаг 3: Удаление файлов из истории
```bash
# Создать 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: Проверка целостности (ОБЯЗАТЕЛЬНО!)
```bash
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 метод + прямое копирование на сервер**
```bash
# 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/hunabapp.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/dev\" > 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 dev'"
```
**✅ Результат:** Gitea репозиторий обновлен очищенной версией
#### Шаг 6: Обновление локального репозитория
```bash
# Заменить .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 файлов).
**Симптомы:**
```bash
git ls-tree -r HEAD --name-only | wc -l
# Результат: 0 (ВСЕ КОММИТЫ ПУСТЫЕ!)
git log --oneline -5
# Коммиты есть, но в них НЕТ ФАЙЛОВ
git status
# Показывает ВСЕ файлы как untracked
```
**Причина:** `--invert-paths` удалил указанные пути из ВСЕХ коммитов. Если эти пути были в каждом коммите, все коммиты стали пустыми.
**✅ Решение:**
```bash
# 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`
### 🔄 Восстановление из резервной копии
Если что-то пошло не так:
```bash
# Восстановить локальный репозиторий
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 через скрипт: `bash scripts/deployment/gitea/git-push-gitea-via-proxy.sh dev`
- [ ] Проверено что файлы больше не отслеживаются: `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 с описанием операции
### 📚 Связанные документы
- **[Deployment Changelog](../CHANGELOG.md)** - История изменений deployment (включает очистку истории)
- **[Git Filter Repo Documentation](https://github.com/newren/git-filter-repo)** - Официальная документация git-filter-repo
---
**Последнее обновление:** 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 без сетевых запросов