Fast and powerful Git hooks manager for any type of projects.

Build Status

Lefthook

The fastest polyglot Git hooks manager out there

Fast and powerful Git hooks manager for Node.js, Ruby or any other type of projects.

  • Fast. It is written in Go. Can run commands in parallel.
  • Powerful. With a few lines in the config you can check only the changed files on pre-push hook.
  • Simple. It is single dependency-free binary which can work in any environment.

📖 Read the introduction post

# On `git push` lefthook will run spelling and links check for all of the changed files
pre-push:
  parallel: true
  commands:
    spelling:
      files: git diff --name-only HEAD @{push}
      glob: "*.md"
      run: npx yaspeller {files}
    check-links:
      files: git diff --name-only HEAD @{push}
      glob: "*.md"
      run: npx markdown-link-check {files}
Sponsored by Evil Martians

Usage

Choose your environment:

Then you can find all Lefthook features in the full guide and explore wiki.


Why Lefthook

  • Parallel execution

If you want more speed. Example

pre-push:
  parallel: true
  • Flexible list of files

If you want your own list. Custom and prebuilt examples.

pre-commit:
  commands:
    frontend-linter:
      run: yarn eslint {staged_files}
    backend-linter:
      run: bundle exec rubocop --force-exclusion {all_files}
    frontend-style:
      files: git diff --name-only HEAD @{push}
      run: yarn stylelint {files}
  • Glob and regexp filters

If you want to filter list of files. You could find more glob pattern examples here.

pre-commit:
  commands:
    backend-linter:
      glob: "*.rb" # glob filter
      exclude: "application.rb|routes.rb" # regexp filter
      run: bundle exec rubocop --force-exclusion {all_files}
  • Execute in sub-directory

If you want to execute the commands in a relative path

pre-commit:
  commands:
    backend-linter:
      root: "api/" # Careful to have only trailing slash
      glob: "*.rb" # glob filter
      run: bundle exec rubocop {all_files}
  • Run scripts

If oneline commands are not enough, you can execute files. Example.

commit-msg:
  scripts:
    "template_checker":
      runner: bash
  • Tags

If you want to control a group of commands. Example.

pre-push:
  commands:
    packages-audit:
      tags: frontend security
      run: yarn audit
    gems-audit:
      tags: backend security
      run: bundle audit
  • Support Docker

If you are in the Docker environment. Example.

pre-commit:
  scripts:
    "good_job.js":
      runner: docker run -it --rm  {cmd}
  • Local config

If you a frontend/backend developer and want to skip unnecessary commands or override something into Docker. Description.

# lefthook-local.yml
pre-push:
  exclude_tags:
    - frontend
  commands:
    packages-audit:
      skip: true
  • Direct control

If you want to run hooks group directly.

$ lefthook run pre-commit
  • Your own tasks

If you want to run specific group of commands directly.

fixer:
  commands:
    ruby-fixer:
      run: bundle exec rubocop --force-exclusion --safe-auto-correct {staged_files}
    js-fixer:
      run: yarn eslint --fix {staged_files}
$ lefthook run fixer
  • Optional output

If you don't want to see supporting information:

skip_output:
  - meta #(version and which hook running)
  - success #(output from runners with exit code 0)

Table of contents:

Guides

Migrate from

Examples

Benchmarks

Comparison list

Articles

Owner
Comments
  • Download binary on npm postinstall hook

    Download binary on npm postinstall hook

    As was discussed in issue #16 the lefthook npm package contains all the binaries, this postinstall hook downloads only the appropriate binary for the host OS (or in case of CI: none).

    Note: I submit my PR in accordance to https://github.com/Arkweid/lefthook/issues/16#issuecomment-517672355. Note 2: I did not run any go/ruby command as this PR mainly changes the postinstall script in the .npm directory. If this is still necessary I will update my PR, just let me know what is missing. :v:

  • Pass local and remote branch names as arguments / env variables to pre-push

    Pass local and remote branch names as arguments / env variables to pre-push

    Is it possible to access the local / remote ref names in the pre-push hook?

    Git passes the information as STDIN to the pre-push hook. But lefthook is not passing it to the actual hook.

  • Fixers do not stop commits

    Fixers do not stop commits

    Here's a trivial example:

    FROM ubuntu:bionic
    RUN : \
        && apt-get update \
        && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \
            ca-certificates \
            curl \
            git \
            python3-pip \
            python3-setuptools \
        && apt-get clean
    RUN : \
        && curl --location --silent --output /tmp/lefthook.gz https://github.com/Arkweid/lefthook/releases/download/v0.6.3/lefthook_0.6.3_Linux_x86_64.gz \
        && echo '0876ae7a862cb26aa5bd50173502dfd836cd16317f7af18be0797deeb3e1cfcd  /tmp/lefthook.gz' | sha256sum --check \
        && gunzip /tmp/lefthook.gz \
        && mv /tmp/lefthook /usr/local/bin \
        && chmod +x /usr/local/bin/lefthook \
        && which lefthook
    
    RUN pip3 install autopep8
            
    WORKDIR /src
    
    COPY lefthook.yml .
    
    ENV \
        [email protected] \
        GIT_AUTHOR_NAME='A A' \
        [email protected] \
        GIT_COMMITTER_NAME='A A'
    
    RUN : \
        && git init . \
        && echo 'print(1    +1)' > t.py \
        && lefthook install \
        && git add .
    
    CMD ["bash", "-xc", "git commit -m test && git status && cat t.py"]
    
    pre-commit:
      commands:
        autopep8:
          files: git diff --staged --name-only -- '*.py'
          run: autopep8 -i {files}
    
    docker build -t test .
    docker run --rm -ti test
    
    $ docker run --rm -ti test
    + git commit -m test
    Lefthook v0.6.3
    RUNNING HOOKS GROUP: pre-commit
    
      EXECUTE > autopep8
    
    SUMMARY: (done in 0.17 seconds)
    ✔️  autopep8
    [master (root-commit) 2858089] test
     2 files changed, 6 insertions(+)
     create mode 100644 lefthook.yml
     create mode 100644 t.py
    + git status
    On branch master
    Changes not staged for commit:
      (use "git add <file>..." to update what will be committed)
      (use "git checkout -- <file>..." to discard changes in working directory)
    
    	modified:   t.py
    
    no changes added to commit (use "git add" and/or "git commit -a")
    + cat t.py
    print(1 + 1)
    

    The commit should have been blocked as autopep8 modified a file

  • Npm: download the required binaries during installation

    Npm: download the required binaries during installation

    • The binaries are now downloaded upon installation. This reduces the package size significantly, which is very important for open-source repositories.

    • Fixes finding the binaries on Windows inside the hooks. Previously the code fell back to using npx

    Closes #43

  • Don't run tool if no file to work on

    Don't run tool if no file to work on

    Hello,

    Possible bug I saw during test lefthook.

    Actual behavior

    When no {staged_files} is empty (SKIP. NO FILES FOR INSPECTING) is output but the command is called.

    Waited behavior

    The command is not called

    Herve-M

  • Lefthook for Dart

    Lefthook for Dart

    Hello! I want to use lefthook with Dart. I can make a PR for that, but I have a couple of questions.

    Do you publish all packages manaully? Who will publish new versions to pub (Dart package registry)?

    P.S. Thanks for lib, it is really great! 😻

  • Speed up on Node

    Speed up on Node

    Now we have 3 binaries in npm and script which run necessary binary depends on the current platform.

    There are 2 big problems with current behavior:

    1. npm package size is huge (26 MB)
    2. Node.js startup time is not great.

    Solution:

    1. postinstall script download necessary binary (many npm packages use this approach, for instance puppeter).
    2. npm runs binary directly without index.js
  • wiki: Comparison page

    wiki: Comparison page

  • npm Scope package naming

    npm Scope package naming

    Since this project moved into Evil Martians a long time ago, I would like to propose to rename or alias the npm package scope to just "lefthook".

  • Lefthook with Git GUI

    Lefthook with Git GUI

    I'm currently using Husky and lint-staged with Tower, hooks with this setup work

    I want to try Lefthook, but having problems

    Tower https://www.git-tower.com/mac

    set hooks with this command

    [~/kit]$ yarn lefthook install -a
    yarn run v1.21.1
    $ /node_modules/.bin/lefthook install -a
    SYNCING lefthook.yml
    SERVED HOOKS: pre-commit, prepare-commit-msg
    ✨  Done in 0.45s.
    
    [~/kit/.git/hooks]$ ls -la 
    total 16
    drwxr-xr-x   4 pavel  staff  128 Dec 24 17:01 .
    drwxr-xr-x  17 pavel  staff  544 Dec 24 17:01 ..
    -rwxr-xr-x   1 pavel  staff  370 Dec 24 17:01 pre-commit
    -rwxr-xr-x   1 pavel  staff  618 Dec 24 17:01 prepare-commit-msg
    

    made one commit via Tower, linters have worked, but after this hooks have been deleted

    [~/kit/.git/hooks]$ ls -la 
    total 0
    drwxr-xr-x   2 pavel  staff   64 Dec 24 17:03 .
    drwxr-xr-x  17 pavel  staff  544 Dec 24 17:03 ..
    

    unfortunately could not find a way which git commands uses Tower when committing

    Source tree https://www.sourcetreeapp.com/

    Husky and lint-staged

    git --no-optional-locks -c color.branch=false -c color.diff=false -c color.status=false -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree commit -q -F /var/folders/kj/w787fzx160130jtykf3kj1440000gn/T/SourceTreeTemp.Io8AzJ -a 
    Info: can't find node in PATH, trying to find a node binary on your system
    husky > pre-commit (node v13.5.0)
    Stashing changes... [started]
    Stashing changes... [skipped]
    → No partially staged files found...
    Running tasks... [started]
    Running tasks for src/**/*.{js,jsx} [started]
    Running tasks for src/**/.{scss,css} [started]
    Running tasks for src/**/.{scss,css} [skipped]
    → No staged files match src/**/.{scss,css}
    prettier --write [started]
    prettier --write [completed]
    eslint --fix [started]
    eslint --fix [completed]
    git add [started]
    git add [completed]
    Running tasks for src/**/*.{js,jsx} [completed]
    Running tasks... [completed]
    

    Lefthook hooks did not work when committing

    git --no-optional-locks -c color.branch=false -c color.diff=false -c color.status=false -c diff.mnemonicprefix=false -c core.quotepath=false -c credential.helper=sourcetree commit -q -F /var/folders/kj/w787fzx160130jtykf3kj1440000gn/T/SourceTreeTemp.fvWmZQ -a 
    Can't find lefthook in PATH
    Can't find lefthook in PATH
    Completed successfully
    

    System info

    macOS 10.15.2
    git 2.24.1
    lefthook 0.7.0
    

    Do you know why hooks can disappear after a commit?

  • Documentation to work with commitlint?

    Documentation to work with commitlint?

    Unfortunately the commitlint documentation only has these instructions:

    {
      "husky": {
        "hooks": {
          "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
        }
      }
    }
    

    And it's unclear whether lefthook exposes whatever HUSKY_GIT_PARAMS are in such a way that it works with commitlint.

  • Reports no errors when running invalid/missing commands

    Reports no errors when running invalid/missing commands

    :wrench: Summary

    When running invalid/missing command, lefthook doesn't report any errors.

    Lefthook version

    1.2.6

    Steps to reproduce

    $ lefthook run ammaabsent
    Lefthook v1.2.6
    RUNNING HOOK: ammaabsent
    $ echo $?                
    0
    

    Expected results

    $ lefthook run ammaabsent
    Lefthook v1.2.6
    RUNNING HOOK: ammaabsent
    ERROR: no such hook `ammaabsent`
    $ echo $?                
    1
    
  • Stage changed files skip unstaged changes

    Stage changed files skip unstaged changes

    :zap: Summary

    • [x] Refactor runner.go to be more readable
    • [x] Add pre- and post-run handlers
    • [ ] Stash unstaged changes for pre-commit hook before running the hook and unstash them after

    For reference:

  • Error using {push_files}

    Error using {push_files}

    Summary

    Whenever I pass the {push_files} argument to a run: in the pre-push section I just get this error message that says error replacing {push_files}: exit status 128

    Steps to reproduce

    Here's the relevant sections my lefthook.yaml file:

    ---
    skip_output:
      - meta       # Skips lefthook version printing
      - success    # Skips successful steps printing
    pre-push:
      parallel: true
      commands:
        shellcheck:
          tags: sast
          glob: "*.sh"
          run: scripts/shellcheck.sh {push_files}
    

    Contents of shellscheck.sh though I'm sure it doesn't matter because it happens no matter what I use but adding for completeness sake:

    #!/usr/bin/env bash
    set -euo pipefail
    
    if ! command -v shellcheck > /dev/null; then
      echo "WARN: shellcheck is not installed. Installing....."
      if [[ "$(uname)" == "Darwin" ]]; then
        if ! command -v brew > /dev/null; then
          echo "ERROR: Homebrew isn't installed. Please see https://docs.brew.sh/Installation for instructions."
          exit 1
        else
          brew install -q shellcheck
        fi
      else
        echo "ERROR: Non macOS operating systems not supported at this time. Feel free to open a PR and extend this check :)"
      fi
    fi
    
    for file in "$@"
    do
      shellcheck -o all "${file}"
    done
    

    Expected results

    I expect it to not error? lol. I know the file i'm testing should cause an error to return from my script. I've also tried other scripts in the run directive and the same results.. anytime I reference the {push_files} I get that same error.

    Actual results

    I make adjustments to a *.sh file and when I try to push up the change to remote up I get the error [snipped branch name details]:

    ➜  git push origin BRANCH_NAME
    error replacing {push_files}: exit status 128
    shellcheck: (SKIP. ERROR)
    
    refs/heads/style/BRANCH_NAME branch_sha refs/heads/style/BRANCH_NAME branch_sha
    
    SUMMARY: (done in 0.09 seconds)
    

    Possible Solution

    🤷🏻

  • Removing a ref after lefthook install fails to fetch from the main branch

    Removing a ref after lefthook install fails to fetch from the main branch

    Summary

    When you remove a ref option from remote configuration, and you already had lefthook install-ed, the remote syncing fails.

    Steps to reproduce

    remote:
      git_url: https://github.com/evilmartians/lefthook
      ref: v1.1.1
      config: examples/complete/lefthook.yml
    
    $ lefthook install
    SYNCING
    SERVED HOOKS: commit-msg, pre-commit, pre-push
    $ git -C .git/info/lefthook-remotes/lefthook/ log --oneline
    55f4381 (grafted, HEAD, tag: v1.1.1) 1.1.1: Quote path to script
    
    remote:
      git_url: https://github.com/evilmartians/lefthook
      config: examples/complete/lefthook.yml # assumed to fetch from master branch 
    
    $ lefthook install
    SYNCING
    SERVED HOOKS: commit-msg, pre-commit, pre-push
    $ git -C .git/info/lefthook-remotes/lefthook/ log --oneline
    55f4381 (grafted, HEAD, tag: v1.1.1) 1.1.1: Quote path to script # (!) must be master branch
    

    Expected results

    Expected to checkout to the main branch of the repository. May be master or main branch at least.

    Possible Solution

    Provide a default value for the ref.

  • support new config file format

    support new config file format

    It will be really cool to add .lefthook.yml (with dot in the beginning) config file format support. Just to align with other libs:

  • Git submodules doesn't appear in changed files

    Git submodules doesn't appear in changed files

    post-checkout:
      commands:
        update-submodules:
          run: printf '%s\n' {all_files}
    

    I expected the submodules we're using to appear in the {all_files} but it wasn't the case. I think it should display the submodules as well.

Gum - Git User Manager (GUM) - Switch between git user profiles
Gum - Git User Manager (GUM) - Switch between git user profiles

Git User Manager (GUM) Add your profile info to config.yaml Build project: go bu

Feb 14, 2022
Git with a cup of tea, painless self-hosted git service
Git with a cup of tea, painless self-hosted git service

Gitea - Git with a cup of tea View the chinese version of this document Purpose The goal of this project is to make the easiest, fastest, and most pai

Jan 2, 2023
ReGit: A Tiny Git-Compatible Git Implementation written in Golang

ReGit is a tiny Git implementation written in Golang. It uses the same underlying file formats as Git. Therefore, all the changes made by ReGit can be checked by Git.

Oct 31, 2022
A Git RPC service for handling all the git calls made by GitLab
A Git RPC service for handling all the git calls made by GitLab

Quick Links: Roadmap | Want to Contribute? | GitLab Gitaly Issues | GitLab Gitaly Merge Requests | Gitaly is a Git RPC service for handling all the gi

Nov 13, 2021
A simple cli tool for switching git user easily inspired by Git-User-Switch
A simple cli tool for switching git user easily inspired by Git-User-Switch

gitsu A simple cli tool for switching git user easily inspired by Git-User-Switch Installation Binary releases are here. Homebrew brew install matsuyo

Dec 31, 2022
Removes unnecessarily saved git objects to optimize the size of the .git directory.

Git Repo Cleaner Optimizes the size of the .git directory by removing all of the files that are unnecessarily-still-saved as part of the git history.

Mar 24, 2022
Git-now-playing - Git commits are the new AIM status messages

git-now-playing git-now-playing is an attempt to bring some of the panache of th

Apr 4, 2022
Git-Go is a supposed cross platform website blocker that will block any web domain by looping the connection to the loopback
Git-Go is a supposed cross platform website blocker that will block any web domain by looping the connection to the loopback

git-go Git-Go is a supposed cross platform website blocker that will block any web domain by looping the connection to the loopback ______ _____ ____

May 19, 2022
A Simple and Comprehensive Vulnerability Scanner for Container Images, Git Repositories and Filesystems. Suitable for CI
A Simple and Comprehensive Vulnerability Scanner for Container Images, Git Repositories and Filesystems. Suitable for CI

A Simple and Comprehensive Vulnerability Scanner for Containers and other Artifacts, Suitable for CI. Abstract Trivy (tri pronounced like trigger, vy

Jan 9, 2023
A tool to monitor git repositories and automatically pull & push changes

git-o-matic A tool to monitor git repositories and automatically pull & push changes Installation Packages & Binaries Arch Linux: gitomatic Binaries f

Dec 20, 2022
go mod vendor lets you check in your dependencies to git, but that's both bloaty (for developers) and tedious (remembering to update it).

go-mod-archiver Afraid of being unable to build historical versions of your Go program? go mod vendor lets you check in your dependencies to git, but

Dec 1, 2022
A single Git repository that holds two microservices (Python and GO)

A single Git repository that holds two microservices (Python and GO)

Nov 19, 2021
Installs git repos onto your system and keeps them up-to-date

Gitfile Installs git repos onto your system and keeps them up-to-date. It's a lightweight package manager for things that haven't been published to a

Jan 16, 2021
Sync tags in your git repository and a changelog in Keep a Changelog format with releases of your GitLab project.

Automatic GitLab releases Sync tags in your git repository and a changelog in Keep a Changelog format with releases of your GitLab project. Features:

Nov 12, 2022
Gogs is a painless self-hosted Git service
Gogs is a painless self-hosted Git service

Gogs - A painless self-hosted Git service 简体中文 ?? Vision The Gogs (/gɑgz/) project aims to build a simple, stable and extensible self-hosted Git servi

Jan 9, 2023
A highly extensible Git implementation in pure Go.
A highly extensible Git implementation in pure Go.

go-git is a highly extensible git implementation library written in pure Go. It can be used to manipulate git repositories at low level (plumbing) or

Jan 8, 2023
commit/branch/workdir explorer for git

gitin gitin is a commit/branch/status explorer for git gitin is a minimalist tool that lets you explore a git repository from the command line. You ca

Dec 31, 2022
A command-line tool that makes git easier to use with GitHub.

hub is a command line tool that wraps git in order to extend it with extra features and commands that make working with GitHub easier. For an official

Jan 1, 2023
SQL interface to git repositories, written in Go. https://docs.sourced.tech/gitbase

gitbase gitbase, is a SQL database interface to Git repositories. This project is now part of source{d} Community Edition, which provides the simplest

Dec 25, 2022