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.gitIt 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.gitNow, 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.