Logo

pre-commit 도구로 Git Hook 사용하기

Git의 pre-commit 훅(hook)은 우리가 작성한 코드를 커밋할 때 마다 자동으로 특정 작업을 실행해줍니다. 많은 프로젝트들이 이를 통해 포맷터(formatter)를 실행하여 코드 스타일을 통일하고, 린터(linter)를 실행하여 코드에 잠재하고 있는 문제들을 찾아냅니다.

이번 포스팅에서는 Git의 pre-commit hook을 편리하게 사용할 수 있도록 도와주는 pre-commit라는 도구에 대해서 알아보겠습니다.

설치

pre-commit은 자신의 컴퓨터에 파이썬이 설치가 되어 있다면 파이썬의 패키지 매니저인 pip를 사용하여 설치할 수 있습니다.

$ pip install pre-commit
Collecting pre-commit
  Downloading pre_commit-2.8.2-py2.py3-none-any.whl (184 kB)
     |████████████████████████████████| 184 kB 3.2 MB/s
Collecting toml
  Downloading toml-0.10.2-py2.py3-none-any.whl (16 kB)
Requirement already satisfied: pyyaml>=5.1 in /opt/virtualenvs/python3/lib/python3.8/site-packages (from pre-commit) (5.3.1)
Collecting cfgv>=2.0.0
  Downloading cfgv-3.2.0-py2.py3-none-any.whl (7.3 kB)
Collecting nodeenv>=0.11.1
  Downloading nodeenv-1.5.0-py2.py3-none-any.whl (21 kB)
Collecting virtualenv>=20.0.8
  Downloading virtualenv-20.1.0-py2.py3-none-any.whl (4.9 MB)
     |████████████████████████████████| 4.9 MB 4.6 MB/s
Collecting identify>=1.0.0
  Downloading identify-1.5.9-py2.py3-none-any.whl (97 kB)
     |████████████████████████████████| 97 kB 7.4 MB/s
Collecting distlib<1,>=0.3.1
  Downloading distlib-0.3.1-py2.py3-none-any.whl (335 kB)
     |████████████████████████████████| 335 kB 43.6 MB/s
Requirement already satisfied: six<2,>=1.9.0 in /opt/virtualenvs/python3/lib/python3.8/site-packages (from virtualenv>=20.0.8->pre-commit) (1.15.0)
Requirement already satisfied: appdirs<2,>=1.4.3 in /opt/virtualenvs/python3/lib/python3.8/site-packages (from virtualenv>=20.0.8->pre-commit) (1.4.4)
Collecting filelock<4,>=3.0.0
  Downloading filelock-3.0.12-py3-none-any.whl (7.6 kB)
Installing collected packages: toml, cfgv, nodeenv, distlib, filelock, virtualenv, identify, pre-commit
Successfully installed cfgv-3.2.0 distlib-0.3.1 filelock-3.0.12 identify-1.5.9 nodeenv-1.5.0 pre-commit-2.8.2 toml-0.10.2 virtualenv-20.1.0
WARNING: You are using pip version 20.1.1; however, version 20.2.4 is available.
You should consider upgrading via the '/opt/virtualenvs/python3/bin/python3 -m pip install --upgrade pip' command.

파이썬이 설치되어 있지 않다면, 운영체제의 패키지 매니저를 통해서도 설치할 수 있습니다. 예를 들어, MacOS 사용자는 Homebrew를 사용하면 됩니다.

$ brew install pre-commit

설치가 제대로 되었는지 확인해보기 위해서 버전을 출력해봅니다.

$ pre-commit -V
pre-commit 2.8.2

설정

pre-commit은 .pre-commit-config.yaml이라는 설정 파일을 필요로 합니다.

터미널에 샘플 설정을 출력해주는 커맨드를 이용하여 설정 파일을 생성해보겠습니다.

$ pre-commit sample-config > .pre-commit-config.yaml

생성된 설정 파일을 열어보면, 4개의 훅(hook)이 설정되어 있는 것을 볼 수 있습니다.

# See https://pre-commit.com for more information
# See https://pre-commit.com/hooks.html for more hooks
repos:
  - repo: https://github.com/pre-commit/pre-commit-hooks
    rev: v3.2.0
    hooks:
      - id: trailing-whitespace
      - id: end-of-file-fixer
      - id: check-yaml
      - id: check-added-large-files

pre-commit 도구는 인터넷에 공개되어 있는 Git 저장소로 부터 hook을 내려받아서 실행을 합니다. 즉, 우리는 실행하고 싶은 hook이 어느 Git 저장소에 위치하는지 알아야 합니다. pre-commit 공식 홈페이지의 Supported hooks 페이지를 방문하시면 커뮤니티에서 제공하는 다양한 pre-commit hook을 만나볼 수 있습니다.

실행

보통 초기 셋업 단계에서는 직접 pre-commit를 실행해보면서 설정이 잘 되었는지 확인이 필요합니다.

수동으로 pre-commit을 실행해주는 커맨드를 터미널에서 실행해보겠습니다.

$ pre-commit run
[INFO] Initializing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Installing environment for https://github.com/pre-commit/pre-commit-hooks.
[INFO] Once installed this environment will be reused.
[INFO] This may take a few minutes...
Trim Trailing Whitespace.............................(no files to check)Skipped
Fix End of Files.....................................(no files to check)Skipped
Check Yaml...........................................(no files to check)Skipped
Check for added large files..........................(no files to check)Skipped

설정 파일에 등록된 모든 hook이 설치된 후, 체크할 파일이 없어서 모든 hook의 실행이 생략되는 것을 볼 수 있습니다.

설정 파일을 스테이징 영역에 추가하고 다시 pre-commit 도구를 실행해보겠습니다.

$ git add .pre-commit-config.yaml
$ pre-commit run -a
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...............................................................Passed
Check for added large files..............................................Passed

이번에는 모든 hook이 통과했다고 나오는 것을 확인할 수 있습니다. 본격적인 테스트를 위해 설정 파일을 커밋하겠습니다.

$ git commit -m "creates .pre-commit-config.yaml"
[master (root-commit) d262bdb] creates .pre-commit-config.yaml
 1 file changed, 10 insertions(+)
 create mode 100644 .pre-commit-config.yaml

테스트

설정한 hook이 제대로 동작하는지 체크하기 위해서, 일부로 불필요한 공백(trailing whitespace)을 넣어서 test.txt 파일을 생성해보겠습니다.

$ echo "test " > test.txt

생성된 파일을 스테이징 영역에 추가하고 pre-commit 도구를 실행해보면 trailing-whitespace hook이 실패한 것을 확인할 수 있습니다.

$ git add test.txt
$ pre-commit run
Trim Trailing Whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing test.txt

Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped
Check for added large files..............................................Passed

이렇게 hook이 실패한 파일은 자동으로 수정해 주기 때문에 변경분을 스테이징 영역에 추가 후, 다시 pre-commit를 실행하면 모든 hook이 통과하는 것을 볼 수 있습니다.

$ git status
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   test.txt

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:   test.txt

$ git add test.txt
$ pre-commit run
Trim Trailing Whitespace.................................................Passed
Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped
Check for added large files..............................................Passed

자동화

pre-commit를 사용하는 최종 목적은 Git으로 커밋(commit)을 남길 때 마다 특정 작업을 자동으로 실행하는 것입니다. 이를 위해서는 프로젝트의 모든 개발자들이 Git 저장소를 클론받은 후에 제일 먼저 pre-commit install 커맨드를 실행해야 합니다.

$ pre-commit install

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

이 명령어를 실행한 후에는 매번 pre-commit run 커맨드를 실행하지 않아도, 커밋을 할려고할 때 마다 자동으로 pre-commit이 실행이 됩니다.

$ echo "test " > test.txt
$ git add test.txt
$ git commit -m "adds test"
Trim Trailing Whitespace.................................................Failed
- hook id: trailing-whitespace
- exit code: 1
- files were modified by this hook

Fixing test.txt

Fix End of Files.........................................................Passed
Check Yaml...........................................(no files to check)Skipped

마치면서

이상으로 pre-commit 도구를 어떻게 사용하는지에 대해서 간단히 살펴보았습니다. pre-commit의 가장 큰 장점은 Git이 언어에 구애받지 않듯이, pre-commit도 언어와 상관없이 사용할 수 있다는 것입니다. 추후 기회가 되면 언어별로 자주 사용되는 hook에 대해서 다뤄보도록 하겟습니다.