Gogs was once a great project, but it’s since been left in the dust both feature-wise and stability-wise by the community-led Gitea. At this point, it’s disingenuous to label Gitea a “fork”; it’s really come into its own with a feature set inspired by GitHub’s.

Since the two projects diverged some time ago, upgrading to Gitea – especially from recent versions of Gogs – isn’t entirely straightforward. With some help from Gitea’s GitHub issues and a little poking around the database myself, I’ve just wrapped up a migration from Gogs 0.11.86 to Gitea 1.12.1. Here are the steps I followed to do it.

0. Back up Gogs and dump the database

Running gogs backup before starting a risky upgrade process is a no-brainer, but you’ll want to grab a database dump before you continue too.

This is because – in case you needed yet another reason to move from Gogs to Gitea – the Gogs restore-from-backup process doesn’t work.

To its credit, it works well enough to get your most critical data back most of the time, but if you’re anything like me, a partial restore isn’t enough to give you complete confidence in a backup solution.

gogs backup doesn’t perform a database dump, at least not in the traditional sense. Instead, it queries the database for information and stores it in a database-agnostic JSON format. (It stores this in a strange way – instead of each file being a valid JSON object containing an array of entries, each line in each file is a JSON object describing a single entry – careful if you have to parse it!) This sounds great in theory since, if it worked, it would let you migrate from one database to another by doing a simple backup and restore!

The thing is, if you’re going to come up with a solution that’s “better” than a tried-and-true database dump, you better make sure that solution is well-tested, or at least coupled with a database dump as a fallback option. gogs backup produces no database dump, and as for being well-tested... you need look no further than Gogs’s GitHub issues on backup and restore to see how well this works in practice. Failure to backup or restore from backup should be considered a critical, blocking issue, since people are relying on that process to keep their data safe, yet these problems are treated like any other bug.

So don’t be like me: perform a mysqldump or pg_dump in addition to a gogs backup before you embark on this journey. I was able to get my data back to normal by writing my own scripts to fill in the gaps that gogs restore did not, but this was through sheer luck more than anything else.

1. Follow the official migration document

The “Upgrade from Gogs” document is designed for Gogs versions 0.9.146 and older, but the notes around renaming folders and so on are still relevant, so follow its guidance to get your files in the right places. With one important caveat: do not run gitea web yet. We need to get the data in order first.

2. Override the database schema version

Gogs and Gitea diverged some time ago, now, so you’ll need to back the schema version down to 13 for Gitea’s migrations to run properly.

If you don’t do this, the final version of the database will be missing critical information from earlier migrations. As time goes on, and Gogs diverges further from Gitea, the effects of skipping this step will get worse. In my case, failing to do this resulted in every repository page producing a 400 error due to missing permission information (an empty repo_unit table) from an early migration step.

Connect to your Gogs database and run:

UPDATE version SET version=13;

3. Delete pre- and post-receive webhooks

Recurse through your gitea-repositories folder and delete all of Gogs’s webhooks. If you don’t do this, you’ll end up unable to git pull or git push with perplexing “server gave HTTP response to HTTPS client” or 500 server errors.

# Run in your gitea-repositories folder
find . -type f \( -name pre-receive -o -name post-receive \) -exec rm -f {} \;

4. Download every major Gitea version between 1.0.x and current

To make this process a bit less tedious, here’s some shell.

for version in 1.1.4 1.2.3 1.3.3 1.4.3 1.5.3 1.6.4 1.7.6 1.8.3 1.9.6 1.10.6 1.11.8
do
    wget -O gitea-$version https://dl.gitea.io/gitea/$version/gitea-$version-linux-amd64
    chmod +x gitea-$version
done

5. Run each Gitea version in sequence

Keep your Gitea logs open for this process and watch for any errors. If you’re not sure where Gitea is logging to, check your app.ini.

For each major version of Gitea, run ./gitea-$version web -c /path/to/app.ini. Wait for any database migration steps to finish and load the UI in your browser. At a minimum, confirm that you can log in, view your repositories, view a repository’s file structure, and finally view the contents of a file in one of your repositories.

Put on your favorite Twitch stream and go slowly. This is when issues are most likely to crop up.

If you get into a bad state and need to start over, drop the database and restore from your database dump. The migration process doesn’t appear to touch the repositories, so a full repository wipe and gogs restore probably aren’t needed.

6. Manual database fixes

By now you should have a mostly functional installation of Gitea. Your repositories, pull requests, issues, and such should all be reachable, but I encountered a few additional quirks that needed fixing.

Performing a git push resulted in a missing column error, which was fixed by running:

ALTER TABLE repository ADD COLUMN IF NOT EXISTS is_bare BOOLEAN;

On Gitea 1.12.x, the repository filter on the home page didn’t work on account of the repositories missing an is_archived value. Since this was a fresh migration, I set all my repositories to be unarchived:

UPDATE repository SET is_archived=false;

7. Enjoy Gitea!

That’s it, by now you should have a fully functional Gitea install! Run a gitea dump to save your progress and enjoy using Gitea.