从远古 Gogs 到最新版 Gitea:一次史诗级的 SQLite 数据库迁移踩坑指南

如果你有一个运行了多年的远古版本 Gogs 或老旧版本的 Gitea,想要一步到位升级到最新的 Gitea 1.23.x,那你大概率会经历一场“数据库结构报错的狂轰滥炸”。

底层 ORM 引擎(XORM)在对比旧表结构和新规范时,会遇到各种残缺字段和“幽灵索引”,导致升级脚本死循环卡住。本文记录了我一步步与 SQLite 和 XORM 斗智斗勇,最终实现无损完美迁移的完整实战过程。

⚠️ 核心升级策略:阶梯式跳跃

跨度长达七八年的版本,绝不能直接用最新版覆盖。你需要按照以下阶梯版本依次启动,让数据库结构平滑过渡: 起点 (Gogs/老旧 Gitea) -> v1.6.4 -> v1.14.6 -> v1.19.4 -> 终点 (v1.23.x)

准备工作:

  1. 停掉旧服务。
  2. 绝对必须的步骤:完整备份你的 /data/gitea/data/gitea.db(或相应路径的数据库文件)。
  3. 安装 sqlite3 命令行工具。

🟢 第一阶段:攻克 v1.6.4(打好底座)

下载 Gitea 1.6.4 的二进制文件替换旧文件,运行 ./gitea web。你会遇到以下经典报错:

坑 1:数据表缺失核心字段

报错症状: migrate: do migrate: no such column: id / repo_id ... (通常出现在 protected_branch 等表) 解决思路: 旧表没有自增主键和必要的关联 ID,迁移脚本查不到就崩溃。缺什么我们就补什么。 实操: 进入数据库 sqlite3 data/gitea.db,直接给它补齐:

SQL

ALTER TABLE protected_branch ADD COLUMN id INTEGER DEFAULT 0;
ALTER TABLE protected_branch ADD COLUMN repo_id INTEGER DEFAULT 0;
ALTER TABLE protected_branch ADD COLUMN branch_name TEXT;
ALTER TABLE protected_branch ADD COLUMN can_push INTEGER DEFAULT 0;
ALTER TABLE protected_branch ADD COLUMN created_unix INTEGER;
ALTER TABLE protected_branch ADD COLUMN updated_unix INTEGER;

坑 2:ORM 引擎同步阶段找不到旧索引

报错症状: sync database struct error: no such index: UQE_email_address... 解决思路: XORM 想清理旧版的超长索引,但在你的旧库里没找到。强迫症的它不删掉就不往下走。我们要“立个靶子”让它删。 实操:

SQL

CREATE INDEX UQE_email_address_email_address_user_email_unique ON email_address (email);
CREATE INDEX IDX_email_address_email_address_user_email_unique ON email_address (email);

阶段总结: 当你看到 Macaron (旧版 Web 引擎) 启动并在 3000 端口监听时,说明 v1.6.4 的使命已经完成。按 Ctrl+C 停掉它,准备升级!

🟡 第二阶段:激战 v1.14.6(重构的阵痛)

替换为 v1.14.6 二进制文件,继续运行 ./gitea web。这一步引擎变得更聪明,但也带来了更严格的校验。

坑 3:畸形表导致 UNIQUE constraint failed

报错症状: 在迁移 access_token 时报错 couldn't add in sha1... UNIQUE constraint failed: access_token.sha1解决思路: 历史版本导致的表结构严重畸形(甚至把 NOT 识别成了列名)。由于 Access Token 只用于 API 调用,直接清空数据并删表是最快最安全的,Gitea 会直接重建完美的新表。 实操:

SQL

DELETE FROM access_token;
DROP TABLE access_token;

坑 4:“幽灵索引”与金蝉脱壳

报错症状: index associated with UNIQUE or PRIMARY KEY constraint cannot be dropped 或反复报 index already exists (常出现在 follow 表)。 解决思路: SQLite 的特性是改表名不会连带修改索引名。如果一张非核心表(如 follow 关注关系表)一直卡主,最好的办法是用金蝉脱壳大法:把它改名雪藏,让 Gitea 自动建新表。 实操:

SQL

ALTER TABLE follow RENAME TO follow_old;
-- 如果后续依然报错索引已存在,直接连同索引一起超度:
DROP INDEX IF EXISTS IDX_follow_created_unix;
DROP TABLE IF EXISTS follow_old;

坑 5:升级后仓库全部 404 (You are not allowed to access...)

报错症状: 迁移完进入 Web 界面,发现所有仓库点进去全是 404。 原因: 老旧版本没有 repo_unit(仓库功能单元配置表)。即便建了表里面也是空的,Gitea 认为所有仓库都关闭了“代码”和“工单”功能。 实操: 进入数据库,批量给所有老仓库强行开启对应功能:

SQL

-- 开启代码(Code)面板
INSERT OR IGNORE INTO repo_unit (repo_id, type, config) SELECT id, 1, '{}' FROM repository;
-- 开启工单(Issues)面板
INSERT OR IGNORE INTO repo_unit (repo_id, type, config) SELECT id, 2, '{}' FROM repository;
-- 开启合并请求(Pull Requests)面板
INSERT OR IGNORE INTO repo_unit (repo_id, type, config) SELECT id, 3, '{}' FROM repository;

🔴 第三阶段:冲刺 v1.23.x 最新版(扫清残敌)

平稳跨越 1.14.6 和 1.19.4 后,换上最新版 Gitea 继续跑。最后几步迁移主要集中在性能优化。

坑 6:第三方登录源表的残留

报错症状: Unwrap ldap.Sources failed: table new_login_source_new has no column named uni_login_source_name 实操: 老规矩,雪藏处理。

SQL

ALTER TABLE login_source RENAME TO login_source_old;
DROP INDEX IF EXISTS IDX_login_source_is_actived;
DROP TABLE IF EXISTS login_source_old;

坑 7:最难缠的 action (用户动态) 表

报错症状: 在最后几步(v218, v249, v308, 最终 Sync),不停地报 action 表找不到旧索引(如 IDX_action_idx_action_repo_id)。 解决思路: 引擎在反复尝试重构主页的“用户活动热力图和动态时间线”。你可以选择一直给它立靶子(CREATE INDEX),但如果它实在太顽固,直接物理超度!action 表仅影响首页时间线,不影响代码。 实操:

SQL

DROP TABLE action;

删掉之后,重新运行,引擎会在 Sync 阶段狂喜地建好最新版本的全新 action 表,并且打出 ORM engine initialization successful!

至此,数据库完美升级成功!

🚀 终章:优雅地后台运行

既然底层顽疾已经根除,是时候让它作为稳定的系统服务运行了。

新建 systemd 服务文件:sudo vim /etc/systemd/system/gitea.service

写入以下配置(请根据你的实际路径和启动用户修改):

Ini, TOML

[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target

[Service]
RestartSec=2s
Type=simple
User=git
Group=git
WorkingDirectory=/data/gitea
ExecStart=/data/gitea/gitea web
Restart=always
Environment=USER=git GITEA_WORK_DIR=/data/gitea
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

重载并启动:

Bash

sudo systemctl daemon-reload
sudo systemctl enable gitea
sudo systemctl start gitea

打开浏览器,看着秒开的现代版 Gitea 界面和完好无损的提交记录,享受属于极客的终极多巴胺时刻吧!

发表回复

后才能评论