├── tests ├── __init__.py └── test.py ├── requirements.txt ├── hooks ├── pre-commit.good ├── pre-receive.good ├── pre-commit.bad └── pre-commit.good.py ├── .gitignore ├── requirements_local.txt ├── main_after_hooks.py ├── main_before_hooks.py ├── .pre-commit-config.yaml ├── README.md └── .pre-commit-config_local.yaml /tests/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | pre-commit==2.14.1 2 | -------------------------------------------------------------------------------- /hooks/pre-commit.good: -------------------------------------------------------------------------------- 1 | echo "Hello, this is pre-commit hook!" 2 | -------------------------------------------------------------------------------- /hooks/pre-receive.good: -------------------------------------------------------------------------------- 1 | echo "Hello, this is pre-receive hook!" 2 | -------------------------------------------------------------------------------- /hooks/pre-commit.bad: -------------------------------------------------------------------------------- 1 | echo "Hello, this is pre-commit hook!" 2 | exit 1 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | 3 | *__pycache__ 4 | .mypy_cache 5 | 6 | venv 7 | .idea 8 | -------------------------------------------------------------------------------- /hooks/pre-commit.good.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | print("Hello, this is python pre-commit hook!") 3 | -------------------------------------------------------------------------------- /requirements_local.txt: -------------------------------------------------------------------------------- 1 | black==21.8b0 2 | flake8==3.9.2 3 | isort==5.9.3 4 | mypy==0.910 5 | pre-commit==2.14.1 6 | pre-commit-hooks==4.0.1 7 | -------------------------------------------------------------------------------- /main_after_hooks.py: -------------------------------------------------------------------------------- 1 | import math 2 | import string 3 | 4 | if __name__ == "__main__": 5 | print(math.pi) 6 | print(string.punctuation) 7 | -------------------------------------------------------------------------------- /main_before_hooks.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import string 3 | import math 4 | 5 | if __name__ == "__main__": 6 | print( math.pi ) 7 | print( string.punctuation ) 8 | -------------------------------------------------------------------------------- /tests/test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | 4 | class Test(unittest.TestCase): 5 | def test(self): 6 | 1 / 0 7 | 8 | 9 | if __name__ == "__main__": 10 | unittest.main() 11 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | 3 | - repo: https://github.com/pre-commit/mirrors-isort 4 | rev: v5.6.4 5 | hooks: 6 | - id: isort 7 | args: ["--profile", "black"] 8 | 9 | - repo: https://github.com/psf/black 10 | rev: 20.8b1 11 | hooks: 12 | - id: black 13 | 14 | - repo: https://github.com/pre-commit/pre-commit-hooks 15 | rev: v2.3.0 16 | hooks: 17 | - id: flake8 18 | args: ["--ignore", "E501,W503"] 19 | - id: check-json 20 | - id: check-yaml 21 | - id: name-tests-test 22 | args: ['--django'] 23 | - id: debug-statements 24 | - id: end-of-file-fixer 25 | types: [python] 26 | # - id: no-commit-to-branch 27 | # args: ["--branch", "master", "--branch", "main"] 28 | - id: trailing-whitespace 29 | - id: check-docstring-first 30 | - id: requirements-txt-fixer 31 | - id: check-added-large-files 32 | 33 | - repo: https://github.com/pre-commit/mirrors-mypy 34 | rev: v0.790 35 | hooks: 36 | - id: mypy 37 | 38 | - repo: https://github.com/nbQA-dev/nbQA 39 | rev: 1.3.1 40 | hooks: 41 | - id: nbqa-isort 42 | - id: nbqa-black 43 | - id: nbqa-flake8 44 | args: ["--ignore=E501,E402"] 45 | - id: nbqa-mypy 46 | args: ["--ignore-missing-imports"] 47 | - id: nbqa-pylint 48 | args: ["--disable=E0401,C0413,C0114,C0301"] 49 | 50 | #- repo: local 51 | # hooks: 52 | # - id: unittest 53 | # name: unittest 54 | # entry: python -m unittest discover 55 | # language: python 56 | # always_run: true 57 | # pass_filenames: false 58 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Git Hooks Tutorial 2 | 3 | My public talk about this project at Sberloga:
4 | [**Git Hooks Is All You Need**](https://youtu.be/92OMAtdVIAs) 5 | 6 | ### 1. Git Hooks 101 7 | 8 | 1) Init git repo: 9 | ```shell script 10 | mkdir git_repo 11 | cd git_repo 12 | 13 | git init 14 | ``` 15 | 16 | 2) Git hooks are located in `.git/hooks` folder: 17 | ```shell script 18 | ls -lah .git/hooks 19 | ``` 20 | 21 | 3) Create executable (`chmod +x`) pre-commit hooks (path: `.git/hooks/pre-commit`): 22 | - bash [hook](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/hooks/pre-commit.good) with exit code 0 23 | - bash [hook](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/hooks/pre-commit.bad) with exit code 1 24 | - python [hook](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/hooks/pre-commit.good.py) with exit code 0 25 | 26 | Make commit after each step. You'll see different results. 27 | 28 | ### 2. Python pre-commit library 29 | 30 | 1) Create and activate virtual environment: 31 | ```shell script 32 | python3 -m venv venv 33 | source venv/bin/activate 34 | ``` 35 | 36 | 2) Install and activate pre-commit library: 37 | ```shell script 38 | pip install pre-commit 39 | pre-commit install 40 | ``` 41 | 42 | 3) Create pre-commit configuration file [.pre-commit-config.yaml](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/.pre-commit-config.yaml).
43 | Alternatively, you can use [local configuration](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/.pre-commit-config_local.yaml). 44 | 45 | 46 | 4) Create [main.py](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/main_before_hooks.py) file.
47 | Make commit. Pre-commit library will reformat file according to PEP8.
48 | Remove unused `import sys` string to get ready-to-commit [main.py](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/main_after_hooks.py) file. 49 | 50 | ### 3. Remote repo 51 | 52 | 1) Init empty "remote" repo: 53 | ```shell script 54 | git init --bare ../remote_repo.git 55 | ``` 56 | 57 | 2) Link local and "remote" repo: 58 | ```shell script 59 | git remote add origin ../remote_repo.git 60 | ``` 61 | 62 | 3) Create executable (`chmod +x`) pre-receive [hook](https://github.com/dayyass/git_hooks_is_all_you_need/blob/main/hooks/pre-receive.good) on "remote" (path: `../remote_repo.git/hooks/pre-receive`).
63 | Make push and see the result. 64 | 65 | ### Reference 66 | Useful links: 67 | - git hooks intro: [link](https://githooks.com) 68 | - git hooks usage: [link](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks) 69 | - pre-commit page: [link](https://pre-commit.com) 70 | 71 | ### Thanks 72 | Thanks to [Anastasiya](https://github.com/TabalinaAnastasia) for help with jupyter notebook hooks! 73 | -------------------------------------------------------------------------------- /.pre-commit-config_local.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: local 3 | hooks: 4 | - id: isort 5 | name: isort 6 | entry: isort 7 | language: python 8 | args: ["--profile", "black"] 9 | types: [python] 10 | 11 | - id: black 12 | name: black 13 | entry: black 14 | language: python 15 | types: [python] 16 | 17 | - id: mypy 18 | name: mypy 19 | entry: mypy 20 | language: python 21 | types: [python] 22 | 23 | # pre-commit-hooks 24 | - id: flake8 25 | name: flake8 26 | entry: flake8 27 | language: python 28 | args: ["--ignore", "E501,W503"] 29 | types: [python] 30 | 31 | # pre-commit-hooks 32 | - id: check-json 33 | name: check-json 34 | entry: check-json 35 | language: python 36 | types: [json] 37 | 38 | # pre-commit-hooks 39 | - id: check-yaml 40 | name: check-yaml 41 | entry: check-yaml 42 | language: python 43 | types: [yaml] 44 | 45 | # pre-commit-hooks 46 | - id: name-tests-test 47 | name: name-tests-test 48 | entry: name-tests-test 49 | language: python 50 | args: ['--django'] 51 | files: '.*test.*\.py$' 52 | types: [python] 53 | 54 | # pre-commit-hooks 55 | - id: debug-statements 56 | name: debug-statements 57 | entry: debug-statement-hook 58 | language: python 59 | types: [python] 60 | 61 | # pre-commit-hooks 62 | - id: end-of-file-fixer 63 | name: end-of-file-fixer 64 | entry: end-of-file-fixer 65 | language: python 66 | types: [python] 67 | 68 | # - id: no-commit-to-branch 69 | # name: no-commit-to-branch 70 | # entry: no-commit-to-branch 71 | # language: python 72 | # args: ["--branch", "master", "--branch", "main"] 73 | 74 | # pre-commit-hooks 75 | - id: trailing-whitespace 76 | name: trailing-whitespace 77 | entry: trailing-whitespace-fixer 78 | language: python 79 | types: [python] 80 | 81 | # pre-commit-hooks 82 | - id: check-docstring-first 83 | name: check-docstring-first 84 | entry: check-docstring-first 85 | language: python 86 | types: [python] 87 | 88 | # pre-commit-hooks 89 | - id: requirements-txt-fixer 90 | name: requirements-txt-fixer 91 | entry: requirements-txt-fixer 92 | language: python 93 | files: 'requirements.*\.txt$' 94 | 95 | # pre-commit-hooks 96 | - id: check-added-large-files 97 | name: check-added-large-files 98 | entry: check-added-large-files 99 | language: python 100 | 101 | # - id: unittest 102 | # name: unittest 103 | # entry: python -m unittest discover 104 | # language: python 105 | # always_run: true 106 | # pass_filenames: false 107 | --------------------------------------------------------------------------------