Home Manager - What is the best way to use a long global gitignore file?

I have a very long .gitignore_global file.

Home Manager’s Appendix A. Configuration Options for git ignoring files via programs.git.ignores shows the example:

[
  "*~"
  "*.swp"
]

Looking at Declarative management of dotfiles with Nix and Home Manager example of programs.git.includes :

programs.git = {
    enable = true;
	  includes = [
      { path = "~/.gitlocalconfig"; }
    ];
    ...
  };

Is this how to have git ignore files via Home Manager?

programs.git = {
    enable = true;
	  ignores = [
            "*~"
           "*.swp"
    ];
    ...
  };

If so, would I be better to use the:

  1. include option (as per example above) to include a path to existing .gitignore_global file?

  2. the extraConfig option to set core.excludesFile = ".gitignore_global";

  3. copy everything in my existing .gitignore_global file to whatever the correct format for programs.git.ignores is?

My preference is use nix home manager’s ‘way’/options as much as possible, but the last option - copying everything to programs.git.ignores just appears too fragile and noisy (unless it’s possible to breakout that option to another file?), and I’m confused whether I should use 1 or 2 above instead.

How are you handling large/long global gitignore files with home manager?

Lastly, if I were to use option 2, how is it possible to path my .gitignore_global file located in the directory home manager creates (on my system) ~/.config/git/.gitignore_global?

My dog asked for your dot files repository link :slight_smile:
1 - is ‘ok’ if you have one repository with home config to install in two (or more) machines but for some machine you want different .gitlocalconfig, could be done in a different way but is a matter of taste;

2 - is similar to 1;

for 1 and 2 you could use path expression ./.gitlocalconfig and ./.gitignore_global (without quotes), when home-manager run, it will copy your config files into nix store and replace in your final gitconfig with /nix/store/some-hash-gitignore_global, then git will read from there.

# in your home manager file
{  
  programs.git.enable   = true;
  programs.git.includes = [{ path = ./.gitlocalconfig; } ];
}

3 - is usually the way I do, you can have many files as you want, but you have to import them, there are 3 syntax for that:

3.1. module import:

# in your home manager file
{  
  imports = [ ./gitignore_global.nix ]; 
}
 # gitignore_global.nix
{
  programs.git.ignores = [ 
    #your long list here
  ];
}

3.2. nix import:

# in your home manager file
{
  programs.git.ignores = import ./gitignore_global.nix;
}
# gitignore_global.nix
[ 
  # your long list here
]

3.3. readFile:

# in your home manager file
{lib, ...}: 
{
  programs.git.ignores = lib.splitString "\n" (builtins.readFile ./.gitignore_global);
}

Another solution, would be creating a module that download gitignore from github/gitignore, and expose an interface like this: :hear_no_evil:

# in your home manager file
{  
  imports = [
    ./gitignore_downloader.nix # hard work here
    ./git_please_ignore.nix
  ];
}
{ # ./git_please_ignore.nix
  programs.git.gitignore = {
    enable    = true;
    templates = [ "Global/Archives" "Global/Backup" "Global/Diff"];
    patterns  = [ "**/.data" "**/.direnv" "**/.envrc" ];
  };
}

I did it, isn’t integrated with home-manager, but with devshell, anyway, the code could be used as base.

2 Likes

Thanks @hugosenari!

That’s a great outline of my options and very helpful!!!

PS My dotfiles aren’t online, otherwise I would share.

I’m sorry @hugosenari, I’m struggling to get any option working.

If in my flake.nix’s homeConfiguration’s modules,

I try:

inherit homeDirectory;
            programs.git.ignores = [
              import ${homeDirectory}/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix
            ];

result:

error: syntax error, unexpected DOLLAR_CURLY

       at /nix/store/v9gxlmd89dmq645xs5xijbpfwyyakv2h-source/flake.nix:44:22:

           43|             programs.git.ignores = [
           44|               import ${homeDirectory}/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix
             |                      ^
           45|             ];

which, I guess, is a newb grammar/syntax error.

—————

If I try:

programs.git.enable = true;
            programs.git.ignores = [
              import /Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix
           ];

result:

error: A definition for option `programs.git.ignores."[definition 1-entry 1]"' is not of type `string'. Definition values:
       - In `<unknown-file>': <function>

which, I guess is a home manager restriction that I’m not sure is possible to bypass.

—————

If I put my git config in a git.nix referenced from my flake.nix,

with:

programs.git = {
   enable = true;

…

    extraConfig = {
      init.defaultBranch = "main";
      core.editor = "hx";
      core.fileMode = false;
      core.ignorecase= true;
      core.excludesFiles = import /Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix;
    };

result:

error: access to absolute path '/Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix' is forbidden in pure eval mode (use '--impure' to override), from:

or…

extraConfig = {
      init.defaultBranch = "main";
      core.editor = "hx";
      core.fileMode = false;
      core.ignorecase= true;
      core.excludesFile = [
         import /Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix
      ];
    };

result:

error: A definition for option `programs.git.extraConfig.core.excludesFile."[definition 1-entry 1]"' is not of type `string or boolean or signed integer'. Definition values:
       - In `/nix/store/vgjsj133gi31f42f0ycaha5kjihj40kp-source/home-manager/modules/cli/git.nix': <function>

I likely have tried other approaches after reading your excellent outline of solutions, but my apologies for not documenting them and for missing anything obvious (very likely) from our thread as it’s been a very hard day (dealing with business issues that have been a big distraction).

That why is nice to see your config files :wink: (please don’t share any secret)
I guess the inherit homeDirectory; is not required

There are 3 types of paths in Nix

  1. Absolute path, started with / and nix will warn that you are killing rabbits, they call it impurity
  2. nix paths, are references in other nix packages like "${pkgs.hello}/bin/hello" (you are trying to do this with ${homeDirectory}/.config
  3. Relative path ./bla.nix (in the case of using flake, you have to add them to git with git add bla.nix
I am not good drawing a directory tree :D

X/bla.nix     paths:                ./ble/bli.nix,   ./ble/blo.nix,  ../blu.nix
X/ble/bli.nix paths: ../bla.nix,                     ./blo.nix,      ../../blu.nix
X/ble/blo.nix paths: ../bla.nix,    ./bli.nix,                       ../../blu.nix
blu.nix       paths: ./X/bla.nix,   ./X/ble/bli.nix, ./X/ble/blo.nix

(for flake everything has to be added to git)

I can see your gitignore_global.nix to be sure about it, because if it is already a list, you are importing a list as ONE item into a list ([ [ ".cache" ] ] instead of [ "cache" ]), and isn’t what this config expects. So programs.git.ignores = [ import ./bla.nix ]; is programs.git.ignores = import ./bla.nix;

I think you have something like this:

# flake.nix: 
...
configuration.imports = [ ./home.nix ];
...
# home.nix
{
programs.git.ignores = import ./gitignore_global.nix;
}
#gitignore_global.nix
[
  "avocado"  # nobody want avocados to be added to git
  "cucumber" # I really hope there is no file named cucumber
  "*.exe"    # I'm not Windows user, even if I'm, please don't add binary files
]

Ignore this today, but I almost sure you will face it in other cases:

  • Nix function call are separated by space, ie import ./my.nix
    • call function import with param ./my.nix
  • Lists, are separated by space, ie [ import ./my.nix ]
    • list with two values ìmport and ./my.nix
  • To call a function inside a list, use parenthesis, ie: [ (import ./my.nix) ]

Some resources:

https://nixos.wiki/wiki/Overview_of_the_Nix_Language
https://nix.dev/tutorials/nix-language
https://teu5us.github.io/nix-lib.html#nix-builtin-functions

Thanks @hugosenari, your explanations are amazing - better than many convulted tutorials I have read!

To clarify, I’m using nix/home-manager on macOS.

Perhaps I am doing things wrong, but I am struggling to avoid absolute paths because when I use the following config with relative paths (not below), I get:

error: getting status of '/nix/store/k21jffv6hk8g2245dvns4zn9my3b5aaf-source/home-manager/modules/cli/...': No such file or directory

It appears everything I path to is re-pathed from /nix/store/..., not from my ~/.config directory where I expect home manager to install git in a directory and a gitignores file generated from the file I have pathed to.

Here’s my home manager flake.nix:


  description = "Home Manager configuration of me";

  inputs = {
    # Specify the source of Home Manager and Nixpkgs.
    nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
    home-manager = {
      url = "github:nix-community/home-manager";
      inputs.nixpkgs.follows = "nixpkgs";
    };
  };

  outputs = { nixpkgs, home-manager, ... }:
    let
      username = "me";
      system = "x86_64-darwin";
    in {
      homeConfigurations.${username} = home-manager.lib.homeManagerConfiguration {
        pkgs = nixpkgs.legacyPackages.${system};
        modules = [
          {
            home = {
              inherit username;
              homeDirectory = "/Users/${username}";
              stateVersion = "22.11";
              sessionVariables = {
                EDITOR = "hx";
              };
            };
            programs.home-manager.enable = true;
            programs.helix.enable = true;
          }
            ./home-manager/modules/cli/git.nix
        ];
        # Optionally use extraSpecialArgs
        # to pass through arguments to home.nix
      };
    };

and my git.nix at /Users/me/.config/nixpkgs/home-manager/modules/cli/git.nix:

{ config, pkgs, libs, ... }:
{

  programs.git = {
    enable = true;
    userName = “xxxxxxxxx”;
    userEmail = “xxxxxxxxxx”;

    delta = {
      enable = true;
      options = {
        syntax-theme = "solarized-dark";
        minus-style = "#fdf6e3 #dc322f";
        plus-style = "#fdf6e3 #859900";
        side-by-side = false;
      };
    };

    extraConfig = {
      init.defaultBranch = "main";
      core.editor = "hx";
      core.fileMode = false;
      core.ignorecase= true;
    };
  };
  programs.git.ignores = import /Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix;
}

and my gitignore_global.nix is at /Users/me/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix:

[
  ## MacOS (https://github.com/github/gitignore/blob/4488915eec0b3a45b5c63ead28f286819c0917de/Global/macOS.gitignore)

#### General:
.AppleDouble
.LSOverride

#### Icon must end with two \r :
Icon

#### Thumbnails:
._*

#### Folder view config files:
.DS_Store
.DS_Store?
Desktop.ini

#### Volume Root Files incl. External Drives:
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
.Trashes

#### Directories created on remote AFP shares:
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

*.bak
*.backup
*.gho
*.tmp
]

Perhaps I am doing things wrong, but I am struggling to avoid absolute paths because when I use the following config with relative paths (not below), I get:

Have you created a git repository for your config files?

git init
git add flake.nix
git add ./home-manager/modules/cli/git.nix
git add ./home-manager/modules/cli/gitignore_global.ni

The first thing, nix does to read flake.nix, is copy everything from git, to /nix/store/k21jffv6hk8g2245dvns4zn9my3b5aaf-source/ and try to read your config files from there, or it will give you a error just like: error: getting status of '/nix/store/k21jffv6hk8g2245dvns4zn9my3b5aaf-source/home-manager/modules/cli/...': No such file or directory (k21jffv6hk8g2245dvns4zn9my3b5aaf is a hash that changes everytime you change the content of your config files, that is how we have the rollback feature)

I suppose your structure is: (you forgot to say where is your flake.nix)

./.git                                           # if you don't have a git is your problem
./flake.nix                                      # if you don't add *.nix to git is your problem
./home-manager/modules/cli/git.nix               # at least your flake.nix is expecting it like this
./home-manager/modules/cli/gitignore_global.nix
# try in your terminal
find /nix/store/k21jffv6hk8g2245dvns4zn9my3b5aaf-source/

If you added all files to git, your import of gitignore_global.nix is

programs.git.ignores = import ./gitignore_global.nix; # this path is relative to git.nix

Your gitignore_global.nix is missing quotes

[
  ## MacOS (https://github.com/github/gitignore/blob/4488915eec0b3a45b5c63ead28f286819c0917de/Global/macOS.gitignore)

#### General:
".AppleDouble"
".LSOverride"
### ....
]

It appears everything I path to is re-pathed from /nix/store/... , not from my ~/.config directory where I expect home manager to install git in a directory and a gitignores file generated from the file I have pathed to.

Sure it is, that makes your system reproducible :slight_smile:
nix (and home-manager) does this:

  1. Copy all your repo to /nix/store/{hash}-source/
  2. check the nix code and paths
  3. build dependencies (ie helix)
  4. build your code (config in this case) and generate a /nix/store/{hash}-{pkgname} (yes, your configuration is now a package)
  5. link everything to your profile (ln -s /nix/store/{hash}-{pkgname} /nix/var/profiles/)
  6. link everything again from profile to your home or create files

You are failing in a step < 6, that is why no file has been created for you.

1 Like

“It works!”

Many thanks @hugosenari!!!

That’s another awesome explanation that’s clarified a lot for me (and hopefully anyone else learning nix and home manager).

Sorry, I should have noted my flake.nix is in ~/.config/nixpkgs.

For anyone learning from this, my files are:

~/.config/nixpkgs/.git ← my git repo (all files I am working with are added to it)
~/.config/nixpkgs/flake.nix
~/.config/nixpkgs/flake.lock
~/.config/nixpkgs/home-manager/modules/cli/git.nix
~/.config/nixpkgs/home-manager/modules/cli/gitignore_global.nix

I had tried the (obvious) relative path of the gitignore_global.nix to git.nix in the past, but another error must have convinced me to try the full path and then I got hung up on trying to use a variable (as seen in an earlier note above) to avoid the absolute path error.

Hence your suggestion of:

programs.git.ignores = import ./gitignore_global.nix;

and double quoting strings in the gitignore_global.nix got everything working.

I now have nix/home-manager generated:

~/config/git (replacing my other one - using the home-manager switch -b backup option)
~/config/git/config -> /nix/store/nh9p9jcllrylkif9zvmh49l1i8s7rhz1-home-manager-files/.config/git/config
~/config/git/ignore -> /nix/store/nh9p9jcllrylkif9zvmh49l1i8s7rhz1-home-manager-files/.config/git/ignore

Many thanks again (and to you and @NobbZ and @TLATER for helping me so far too)!

1 Like