Skip to content

Declaratively clone and manage Git/Jujutsu repositories with Home Manager

License

Notifications You must be signed in to change notification settings

rytswd/home-git-clone

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

1 Commit
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

home-git-clone

Declaratively clone and manage Git/Jujutsu repositories with Home Manager

Home Manager module GitHub Source MIT License


πŸŒ„ Overview

home-git-clone is a Home Manager module that lets you define repositories in your Nix configuration and have them automatically cloned during home-manager switch.

Instead of running this on every machine…

git clone git@github.com:user/dotfiles.git ~/Coding/dotfiles
git clone git@github.com:user/project.git ~/Coding/project

Just declare this once:

home.gitClone."Coding/dotfiles".url = "git@github.com:user/dotfiles.git";
home.gitClone."Coding/project".url = "git@github.com:user/project.git";

Run home-manager switch and your repositories appear. Define once, deploy everywhere.

✨ Features

  • Declarative β€” Define repositories in Nix, clone automatically
  • Auto Default Branch β€” Detects β€œmain” vs β€œmaster” automatically
  • Git + Jujutsu β€” Full support for both VCS systems
  • Worktree Ready β€” Optional path structure for easy worktree workflows
  • Smart HTTPS β€” Bypasses git config rewrites for HTTPS URLs
  • Auto-Update β€” Optionally pull/fetch on every activation
  • Multi-User Safe β€” Each user’s credentials, isolated operations
  • GPG Agent β€” Automatic SSH socket detection

πŸš€ Quick Start

1. Add the Module

Using Flakes (Recommended)

# flake.nix
{
  inputs = {
    nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
    home-manager.url = "github:nix-community/home-manager";
    home-git-clone.url = "github:rytswd/home-git-clone";
  };

  outputs = { nixpkgs, home-manager, home-git-clone, ... }: {
    homeConfigurations.youruser = home-manager.lib.homeManagerConfiguration {
      pkgs = nixpkgs.legacyPackages.x86_64-linux;
      modules = [
        home-git-clone.homeManagerModules.default
        ./home.nix
      ];
    };
  };
}

Without Flakes

# home.nix or configuration.nix
{ config, pkgs, ... }:
{
  imports = [
    (builtins.fetchGit {
      url = "https://github.com/rytswd/home-git-clone.git";
      ref = "main";
    } + "/nix")
  ];
}

2. Configure Repositories

# home.nix
{
  home.gitClone = {
    "Coding/dotfiles" = {
      url = "git@github.com:user/dotfiles.git";
    };
  };
}

3. Activate

home-manager switch

Your repository is now at ~/Coding/dotfiles/.

πŸ“– Usage Guide

Git Examples

Basic Clone β€” Simplest configuration with auto-detected branch
home.gitClone = {
  "Coding/my-project" = {
    url = "git@github.com:user/project.git";
  };
};
Result: ~/Coding/my-project/
Specific Branch β€” Clone a specific branch instead of default
home.gitClone = {
  "Coding/my-project" = {
    url = "git@github.com:user/project.git";
    rev = "develop";
  };
};
HTTPS Repository β€” Public repos without SSH keys

HTTPS URLs work seamlessly. Git config rewrites are automatically bypassed.

home.gitClone = {
  "Tools/public-repo" = {
    url = "https://github.com/user/repo.git";
  };
};
Auto-Update β€” Pull latest on every activation

Pull latest changes every time you run home-manager switch.

home.gitClone = {
  "Coding/always-fresh" = {
    url = "git@github.com:user/repo.git";
    update = true;
  };
};
Worktree Mode β€” Structure for git worktree workflows

Appends the branch name to the path, perfect for Git worktree workflows.

home.gitClone = {
  "Coding/my-project" = {
    url = "git@github.com:user/project.git";
    rev = "main";
    useWorktree = true;
  };
};
Result: ~/Coding/my-project/main/

Add more worktrees easily:

cd ~/Coding/my-project/main
git worktree add ../develop
git worktree add ../feature-x
~/Coding/my-project/
  |-- main/        # Created by home-git-clone
  |-- develop/     # Added manually
  +-- feature-x/   # Added manually

Jujutsu Examples

Use home.jjClone for Jujutsu-native workflows.

Basic Clone β€” Git-colocated by default

Creates a Git-colocated repository by default (both .git and .jj directories).

home.jjClone = {
  "Coding/jj-repo" = {
    url = "git@github.com:user/repo.git";
  };
};
Pure Jujutsu β€” Non-colocated, no .git directory

No .git directory, pure Jujutsu only.

home.jjClone = {
  "Coding/pure-jj" = {
    url = "git@github.com:user/repo.git";
    colocate = false;
  };
};
Auto-Update β€” Fetch latest on every activation
home.jjClone = {
  "Coding/jj-fresh" = {
    url = "git@github.com:user/repo.git";
    update = true;  # Runs: jj git fetch
  };
};
Workspace Mode β€” Structure for jj workspace workflows
home.jjClone = {
  "Coding/jj-workspaces" = {
    url = "git@github.com:user/repo.git";
    rev = "main";
    useWorkspace = true;
  };
};
Result: ~/Coding/jj-workspaces/main/

Add more workspaces:

cd ~/Coding/jj-workspaces/main
jj workspace add ../develop

Symlinking Files

Since repositories are cloned directly to your home directory (outside the Nix store), you can symlink files or directories from them using config.lib.file.mkOutOfStoreSymlink.

This is particularly useful for managing configuration files:

{ config, ... }: {
  home.gitClone."Coding/dotfiles" = {
    url = "git@github.com:user/dotfiles.git";
  };

  # Link a directory from the cloned repo to ~/.config
  home.file.".config/nvim".source =
    config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/Coding/dotfiles/nvim";
}

πŸ“‹ Configuration Reference

home.gitClone.<path>

OptionTypeDefaultDescription
urlstringrequiredRepository URL (SSH or HTTPS)
revstring or nullnullBranch/revision. Auto-detects if null.
useWorktreeboolfalseAppend rev to path for worktree structure
bypassGitConfigbool or nullnullOverride git config bypass (auto for HTTPS)
updateboolfalsePull on every activation

home.jjClone.<path>

OptionTypeDefaultDescription
urlstringrequiredRepository URL (SSH or HTTPS)
revstring or nullnullBranch/bookmark. Auto-detects if null.
colocatebooltrueCreate Git-colocated repo (.git + .jj)
useWorkspaceboolfalseAppend rev to path for workspace structure
bypassGitConfigbool or nullnullOverride git config bypass (auto for HTTPS)
updateboolfalseFetch on every activation (jj git fetch)

βš™οΈ Advanced Usage

Override Git Config Bypass β€” Control HTTPS/SSH rewriting behavior

By default, HTTPS URLs bypass your git config to prevent url.*.insteadOf rewrites. Override this behavior:

home.gitClone = {
  # Force bypass even for SSH
  "special/repo1" = {
    url = "git@github.com:user/repo.git";
    bypassGitConfig = true;
  };

  # Use git config even for HTTPS
  "special/repo2" = {
    url = "https://github.com/user/repo.git";
    bypassGitConfig = false;
  };
};
Multi-User Setup β€” Safe for shared systems

Each user runs home-manager with their own credentials. Safe for shared systems.

# User alice (SSH keys)
home.gitClone = {
  "Work/private" = {
    url = "git@github.com:company/private.git";
  };
};

# User bob (HTTPS only)
home.gitClone = {
  "Projects/public" = {
    url = "https://github.com/user/public.git";
  };
};
Combining Git and Jujutsu β€” Use both in the same config

Use both module types in the same configuration:

{
  # Standard Git repositories
  home.gitClone = {
    "Coding/legacy-project" = {
      url = "git@github.com:user/legacy.git";
    };
  };

  # Jujutsu repositories
  home.jjClone = {
    "Coding/modern-project" = {
      url = "git@github.com:user/modern.git";
    };
  };
}

πŸ”§ How It Works

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Your Nix Configuration         β”‚
β”‚  (home.nix / flake.nix)         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  home.gitClone = { ... }        β”‚
β”‚  home.jjClone  = { ... }        β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Home Manager Module            β”‚
β”‚  Validates config, generates    β”‚
β”‚  activation scripts per-repo    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  $ home-manager switch          β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  For each repository:           β”‚
β”‚  1. Check if already cloned     β”‚
β”‚  2. Auto-detect default branch  β”‚
β”‚  3. Setup SSH (GPG agent)       β”‚
β”‚  4. Clone or update             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                β”‚
                β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  βœ“ Repositories ready           β”‚
β”‚    ~/Coding/your-repos/         β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Behaviors

BehaviorDescription
IdempotentSafe to run repeatedly β€” existing repos left untouched
SSH DetectionAuto-uses GPG agent socket at ~/.gnupg/S.gpg-agent.ssh
HTTPS BypassPrevents url.*.insteadOf rewrites for HTTPS URLs
Update ModeOnly fetches/pulls when update = true
Parallel SafeMultiple repos clone independently

⚠️ Known Limitations

No Automatic Deletion

Removing a repository from your configuration does not delete the directory from your disk. This is a deliberate safety feature to prevent accidental data loss. You must manually remove the directory if it is no longer needed.

No URL Validation on Existing Repos

If the target directory already contains a Git repository, the module assumes it is correct. It does not validate that the local repository’s remote URL matches the one specified in your configuration. This ensures that manually modified remotes (e.g., using different push URLs) are not overwritten or flagged as errors.

Sequential Activation

Cloning happens during the activation phase of Home Manager. If you are cloning many large repositories for the first time, home-manager switch will wait for all clones to complete before finishing.

πŸ” Troubleshooting

Repository Not Cloning
  1. Check if the directory already exists:
ls -la ~/path/to/repo
  1. Remove and retry:
rm -rf ~/path/to/repo
home-manager switch
SSH Authentication Fails
  1. Verify your SSH agent:
ssh-add -l
  1. Check GPG agent socket:
ls -la ~/.gnupg/S.gpg-agent.ssh
HTTPS Being Rewritten to SSH

This happens when you have url.*.insteadOf in your git config. The module should bypass this automatically for HTTPS URLs. If not:

home.gitClone."path" = {
  url = "https://github.com/user/repo.git";
  bypassGitConfig = true;  # Force bypass
};

License

MIT

Contributing

Contributions are welcome! Please open an issue or pull request.

About

Declaratively clone and manage Git/Jujutsu repositories with Home Manager

Topics

Resources

License

Stars

Watchers

Forks

Languages