+title: lohr

=lohr= is a Git mirroring tool.

I created it to solve a simple problem I had: I host my own git server at [[https://git.alarsyo.net]], but want to mirror my public projects to GitHub / GitLab, for backup and visibility purposes.

GitLab has a mirroring setting, but it doesn't allow for multiple mirrors, as far as I know. I also wanted my instance to be the single source of truth.

** How it works

Gitea is setup to send webhooks to my =lohr= server on every push update. When =lohr= receives a push, it clones the concerned repository, or updates it if already cloned. Then it pushes the update to all remotes listed in the [[file:.lohr][.lohr]] file at the repo root.

* Destructive

This is a very destructive process: anything removed from the single source of truth is effectively removed from any mirror as well.

** Setup

* Quickstart

Setting up =lohr= should be quite simple:

  1. Create a =Rocket.toml= file and [[https://rocket.rs/v0.4/guide/configuration/][add your configuration]].

  2. Run =lohr=:

    +begin_src sh

    $ cargo run # or cargo run --release for production usage

    +end_src

  3. Configure your favorite git server to send a webhook to =lohr='s address on every push event.

    I used [[https://docs.gitea.io/en-us/webhooks/][Gitea's webhooks format]], but I think they're similar to GitHub and GitLab's webhooks, so these should work too! (If they don't, please file an issue!)

  4. Add a =.lohr= file containing the remotes you want to mirror this repo to:

    +begin_example

    git@github.com:you/your_repo

    +end_example

    and push it. That's it! =lohr= is mirroring your repo now.

* Configuration

** Home directory

=lohr= needs a place to clone repos and store its data. By default, it's the current directory, but you can set the =LOHR_HOME= environment variable to customize it.

** Extra remote configuration

=lohr= looks for a =lohr-config.yaml= file in its =LOHR_HOME= directory. This file takes the following format:

+begin_src yaml

default_remotes: - "git@github:user" - "git@gitlab:user"

additional_remotes: - "git@git.sr.ht:~user"

+end_src

Both settings take as input a list of "stems", i.e. incomplete remote addresses, to which the repo's name will be appended (so for example, if my ~defaultremotes~ contains ~git@github.com:alarsyo~, and a push event webhook is received for repository =git@gitlab.com:some/long/path/reponame=, then the mirror destination will be =git@github.com:alarsyo/repo_name=.

** Contributing

I accept patches anywhere! Feel free to [[https://github.com/alarsyo/lohr/pulls][open a GitHub Pull Request]], [[https://gitlab.com/alarsyo/lohr/-/merge_requests][a GitLab Merge Request]], or [[https://lists.sr.ht/~alarsyo/lohr-dev][send me a patch by email]]!

** Why lohr?

I was looking for a cool name, and thought about the Magic Mirror in Snow White. Some [[https://en.wikipedia.org/wiki/Magic_Mirror_(Snow_White)][furious wikipedia searching]] later, I found that the Magic Mirror was probably inspired by [[http://spessartmuseum.de/seiten/schneewittchen_engl.html][the Talking Mirror in Lohr am Main]]. That's it, that's the story.

** License

=lohr= is distributed under the terms of both the MIT license and the Apache License (Version 2.0).

See [[file:LICENSE-APACHE][LICENSE-APACHE]] and [[file:LICENSE-MIT][LICENSE-MIT]] for details.