├── 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 |
--------------------------------------------------------------------------------