TJKeller.xyz reposync: a home-manager module for managing git repositories imperatively
February 5, 2026
Tags: Git Home Manager Linux My Software Nix

reposync

reposync is a module for home-manager that allows tracking and managing out-of-(nix)store git repositories. The module generates and installs in the users home.packages a shell script called reposync. This command allows managing multiple repositories at once (clone, pull, stow, and more).

You can clone or view the git repository here:
https://git.tjkeller.xyz/hm-reposync

You can also find instructions for installation/usage in the project’s git repository.

Why it’s cool

Home-manager was never meant to replace scriptable configs or run command (rc) files. Your shell, window manager, display server, extensible text editor, and all kinds of other programs rely on these – not to mention home-manager and NixOS themselves!

But, I hear you ask, why not just source these files directly in home-manager? Outside of the fact that this practice is a complete waste of time, it begs the question, how would we even make the files available to source in the first place? A mono-repo could work, but I would prefer bashing my head through several concrete blocks! How about a separate flake for each config repo? Have fun keeping your hashes up-to-date! That’s not to mention the elephant in the room (🐘💬): you have to rebuild the whole thing EVERY SINGLE TIME you make the slightest change! Too bad both of these solutions are absolutely, positively, totally, and completely, a HUGE WASTE OF TIME!

This is why many of our more neuro-divergent friends use tools like Nixvim to configure Neovim. What’s next, nixsh? Not only is this incredibly stupid and perverse, but it is a grave sin against nature itself. Lord have mercy!

Instead, reposync allows you declaratively define an imperative script that you can run whenever you want. It works on the philosophy that, however some may try to portray it, home-manager itself is inherently limited by the fact that your home directory is not immutable. Why bother trying to force home-manager to do something that it wasn’t built for? Does it really need to manage all of your configuration files? Personally, I am happy enough with the fact that I no longer have to think about it! reposync is the practical solution for pragmatic people!

Demonstration

This is what a typical reposync config might look like. The following snippet will clone (or pull if existing) 4 repositories: config, scripts, hm-reposync, and awesome.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
reposync.enable = true;
reposync.outOfStoreGitRepository = {
  config = {
    # Default git server is github (https)
    repository = "github-user/dotconfig";
    target = "src/config";  # Relative to home directory
    extraCloneOptions = "--recurse-submodules";
    stow = {
      vim = {
        targetPrefix = config.xdg.configHome;
      };
      misc = {
        packages = "x11 zsh";
      };
    };
  };
  scripts = {
    # Default repository will be scripts in this case
    server = "https://git.tjkeller.xyz/";
    targetPrefix = "/home/my-user/src";  # Final target will be /home/my-user/src/scripts
    stow."*".target = ".local/bin";
  };
  hm-reposync = {
    enable = lib.mkDefault false;
    server = "https://git.tjkeller.xyz/";
  };
  awesome = {
    server = "https://git.tjkeller.xyz/";
    targetPrefix = config.xdg.configHome;
  };
};

Here is the order of operations drawn out if each repo was synced:

  1. clone config (with --recurse-submodules) → ~/src/config
    a. stow the package vim (vim) → ~/.config/vim (config.xdf.configHome)
    b. stow the package misc (x11 zsh) → ~/x11 and ~/zsh
  2. clone scripts/home/my-user/src/scripts
    a. stow the package * (all packages) → ~/.local/bin/
  3. clone hm-reposync~/hm-reposync (if enabled)
  4. clone awesome~/.config/awesome

Make sure to check the source for the full configuration syntax.

Stow, restow

As an additional note, each time stow is called it runs with the -R or “restow” flag. The restow function is perfect for this use case. It will remove all dangling symlinks and only install the relevant symlinks that are current, much in the same way that home-manager itself works actually. It is the final bit of magic that pulls this whole thing together.