|

Push once, publish everywhere: the multi-target Git remote

Github remains the go-to forge for hosting Git projects today. But as a French guy who lives in Europe, I don’t want to depend on only one foreign platform. So, to reduce that dependency, I decided to mirror all of my repositories (public and private) on Codeberg, a European, non-profit forge.

Usually, people think a Git remote is a simple association with a URL (one remote = one destination). In reality, a remote is two things: a URL for fetching data and another one for pushing data. Using the git remote -v command, it’s possible to visualize a repository configuration:

$ git remote -v
origin  https://github.com/jdecool/repo.git (fetch)
origin  https://github.com/jdecool/repo.git (push)

Git allows to configure multiple push URLs to a remote. It’s what I do to push repository changes to Github and the Codeberg mirror:

To declare the push URLs, use the git remote set-url command with the --add and --push options:

$ git remote set-url --add --push origin git@github.com:jdecool/repo.git
$ git remote set-url --add --push origin ssh://git@codeberg.org/jdecool/repo.git

It produces the following result:

$ git remote -v
origin  https://github.com/jdecool/repo.git (fetch)
origin  https://github.com/jdecool/repo.git (push)
origin  ssh://git@codeberg.org/jdecool/repo.git (push)

Now the origin remote still fetches data from Github, but it also pushes data on two destinations. The Git configuration is stored in the .git/config file:

[remote "origin"]
    url = git@github.com:jdecool/repo.git
    fetch = +refs/heads/*:refs/remotes/origin/*
    pushurl = git@github.com:jdecool/repo.git
    pushurl = ssh://git@codeberg.org/jdecool/repo.git

Now, when I push changes with git push origin main, Git will send commits to both Github and Codeberg. Both repository instances stay up to date.

But there are some important things to notice.

Fist, by default, Git uses the fetch URL for pushing as well. But after you add a first pushurl, the implicit fetch URL is no longer used, it will be replaced. So make sure to declare the original URL alongside the new one.

Secondly, this technique remains a one-way mirror. The fetch URL still contains only one URL. If some code is directly pushed to a secondly instance, those changes won’t be pulled back automatically. In a team context, prefer using the Git mirror feature from the forge server-side.

Jérémy DECOOL

Jérémy DECOOL

As a software architect, I share my thoughts on best practices in software development, software architecture and team organization for maintainable and scalable projects.