Wrapping Git Causes Memory Leak

I want to replace git with my own script. The script does nothing. It calls git as a subprocess.
If this simple script works I will implement a local caching solution for git.

The final binary name in PATH should be also git so all programs using git can benefit from the local cache.
However right now enabling this derivation creates a memory leak and causes system crash. I am not sure why.

{pkgs, ...}:
pkgs.stdenv.mkDerivation {
  name = "git_cached";

  buildInputs = with pkgs; [
    python3
    git
  ];

  dontUnpack = true;
  installPhase = ''
    install -Dm755 ${../scripts/python/git_cached.py} $out/bin/git;
  '';
}

Script:

#!/usr/bin/env python3
import subprocess
import sys


def git_command(args):
    subprocess.run(["git"] + args)


if __name__ == "__main__":
    git_command(sys.argv[1:])

I found the problem. since the package name is also git it is calling itself recursively.
The solution is to change the binary output name of the original git command to something else and then use it in python script.
I have tried this to rename git binary to no avail. nix/store/git_original/out/bin contains git not git_original. So, only the store path is changed.

{pkgs, ...}:
pkgs.stdenv.mkDerivation {
  name = "git_cached";

  propagatedBuildInputs = with pkgs; [
    python3
    (
      let
        git_original = git.overrideAttrs (oldAttrs: {
          pname = "git_original";
        });
      in
        git_original
    )
  ];

  dontUnpack = true;
  installPhase = ''
    install -Dm755 ${../scripts/python/git_cached.py} $out/bin/git;
  '';
}

OK. I have came up with this. It is working and I can safely call git_original in my script.
However any suggestions and simplifications are welcome.

{pkgs, ...}: let
  git_original = pkgs.git.overrideAttrs (oldAttrs: {
    pname = "git_original";
    postInstall =
      oldAttrs.postInstall
      or ""
      + ''
        ln -s $out/bin/git $out/bin/git_original
      '';
  });
in
  pkgs.stdenv.mkDerivation {
    name = "git_cached";

    propagatedBuildInputs = with pkgs; [
      python3
      git_original
    ];

    nativeBuildInputs = [pkgs.makeWrapper];

    dontUnpack = true;
    installPhase = ''
      install -Dm755 ${../scripts/python/git_cached.py} $out/bin/git;
      wrapProgram $out/bin/git --prefix PATH : ${pkgs.lib.makeBinPath [git_original]}
    '';
  }

Wrapping git breaks nixos-rebuild switch. :confused:
Since nix switch is the only one complaining I can use separate git when I am switching: nix-shell -p git.
However it would be better to solve the error.

       … while fetching the input 'git+file:///home/USERNAME/dotfiles'
       error: getting the HEAD of the Git tree '/home/USERNAME/dotfiles' failed with exit code 1:

For some reason bash works. No errors.

#!/usr/bin/env bash

# Call the real git with all arguments
git_original "$@"

I have no idea why but I will probably write my cache tool in bash.

OK. Bash respects stdout/stderr and return values. Thats why it was working. If you do the same in python it works too.


def git(args):
    result = subprocess.run(["git_original"] + args, capture_output=True, text=True)
    print(result.stdout)
    print(result.stderr, file=sys.stderr)
    exit(result.returncode)