# Git Hooks

`devenv` has first-class integration for [pre-commit](https://pre-commit.com/) via [git-hooks.nix](https://github.com/cachix/git-hooks.nix). This integration *requires* to have the `git-hooks` input the `devenv.yaml` file, as in [the inputs page](/inputs/).

If this is not already in the file, add it using `devenv inputs add git-hooks github:cachix/git-hooks.nix` or insert it manually:

**devenv.yml snippet**

```yaml
inputs:
  git-hooks:
    url: github:cachix/git-hooks.nix
```

## Set up

We recommend a two-step approach for integrating your linters and formatters.

### 1) Make sure that commits are well-formatted at commit time

**devenv.nix**

```nix
{ inputs, ... }:


{
  git-hooks.hooks = {
    # lint shell scripts
    shellcheck.enable = true;
    # execute example shell from Markdown files
    mdsh.enable = true;
    # format Python code
    black.enable = true;


    # override a package with a different version
    ormolu.enable = true;
    ormolu.package = pkgs.haskellPackages.ormolu;


    # some hooks have more than one package, like clippy:
    clippy.enable = true;
    clippy.packageOverrides.cargo = pkgs.cargo;
    clippy.packageOverrides.clippy = pkgs.clippy;
    # some hooks provide settings
    clippy.settings.allFeatures = true;
  };
}
```

The full list can be found in the reference.

In action:

```sh
$ devenv shell
Building shell ...
Entering shell ...


pre-commit installed at .git/hooks/pre-commit
```

If you commit a Python or Markdown file or a script, these hooks will run at commit time.

### 2) Verify formatting in CI

Run `devenv test`.

See [the list of all available hooks](/reference/options/#git-hooks).

## Managing the `.pre-commit-config.yaml` file

The `.pre-commit-config.yaml` file is a symlink to an autogenerated file in your `devenv` Nix store. It is not necessary to commit this file to your repository and it can safely be ignored. This file name will be added to your `.gitignore` file by default when you run `devenv init`.

## Adding custom hooks

If you’d like to define your own hook you can do:

**devenv.nix**

```nix
{
  git-hooks.hooks.unit-tests = {
    enable = true;


    # The name of the hook (appears on the report table):
    name = "Unit tests";


    # The command to execute (mandatory):
    entry = "make check";


    # The pattern of files to run on (default: "" (all))
    # see also https://pre-commit.com/#hooks-files
    files = "\\.(c|h)$";


    # List of file types to run on (default: [ "file" ] (all files))
    # see also https://pre-commit.com/#filtering-files-with-types
    # You probably only need to specify one of `files` or `types`:
    types = [ "text" "c" ];


    # Exclude files that were matched by these patterns (default: [ ] (none)):
    excludes = [ "irrelevant\\.c" ];


    # The language of the hook - tells pre-commit
    # how to install the hook (default: "system")
    # see also https://pre-commit.com/#supported-languages
    language = "system";


    # Set this to false to not pass the changed files
    # to the command (default: true):
    pass_filenames = false;
  };
}
```