Moving files across git repositories, preserving history

Posted on August 5, 2026

In today’s exercise in yak shaving, I want to work towards setting up the stalwart mailserver. To do that, I want to import a module from another git repository and want to preserve original authorship of that significant amount of work.

Working across git repos is really not well supported by git and I found few soures about how to do this. The most promising approach I found involved a third-party tool called git-filter-repo. Here is one blog article by Dimitri Kokkonis and another one by Bill Cawthra.

So, let’s try this. I am interested in two files from https://codeberg.org/ungeskriptet/nix-config/src/branch/stalwart. modules/stalwart.nix and machines/ryuzu/stalwart.nix.

I cloned and created a new branch for the rewrite, hoping to keep that repository around for future updates. As I don’t expect to do this kind of git flow often, I simply launched a nix-shell with git-filter-repo:

git clone https://codeberg.org/ungeskriptet/nix-config.git ungeskriptet-nix-config
cd ungeskriptet-nix-config/
git checkout stalwart 
git checkout -b export-stalwart
nix-shell -p git-filter-repo

Since I have created a branch, git-filter-repo does not recognize the repo as a clean clone and requires the --force argument. While modules/stalwart.nix is a reasonble path for my repo layout, machines/ryuzu/stalwart.nix will have to be moved to my repo root:

git filter-repo --force \
  --path modules/stalwart.nix \
  --path machines/ryuzu/stalwart.nix \
  --path-rename machines/ryuzu/stalwart.nix:stalwart.nix
NOTICE: Removing 'origin' remote; see 'Why is my origin removed?'
        in the manual if you want to push back there.
        (was https://codeberg.org/ungeskriptet/nix-config.git)
Parsed 735 commits
New history written in 0.04 seconds; now repacking/cleaning...
Repacking your repo and cleaning out old unneeded objects
HEAD is now at ffac43d stalwart: preparations for v0.16.4 migration
Enumerating objects: 17, done.
Counting objects: 100% (17/17), done.
Delta compression using up to 8 threads
Compressing objects: 100% (8/8), done.
Writing objects: 100% (17/17), done.
Total 17 (delta 2), reused 5 (delta 2), pack-reused 0 (from 0)
Completely finished after 0.07 seconds.

The working directory now contains the two files in the right locations:

ls -R
.:
modules  stalwart.nix

./modules:
stalwart.nix

Now in the repository with my server config, I cautiously created a new branch and tried to merge:

git checkout -b import-stalwart
git remote add ungeskriptet ../ungeskriptet-nix-config/
git fetch ungeskriptet
git merge ungeskriptet/export-stalwart --allow-unrelated-histories

Somewhat to my surprise, that worked. Two new files were created with the history preserved. I have the feeling, that following up with changes will not be that easy, though. For modules/stalwart.nix, creating a patch file with git format-patch and applying that to my repo may be a possible solution. On the other hand, stalwart.nix in my repo is just a starting point and likely won’t be interesting for upstream changes.