git hooks (Client-Side) メモ

# 個人的なメモ
- `pre-commit` の拡張とかやってると「この hook の発火タイミングとか引数どうなってんだ？」となったので整理

# まとめ
- https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks
- https://git-scm.com/docs/githooks

## 発火有無・順序
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/278058/af149fb4-8243-4aee-8669-da6fed218ba9.png)


## hook 詳細
- **pre-commit**
  - タイミング：commit 作成時の最初
  - 用途：commit されるファイルのチェック
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 0 | - | - | - | - |
- **prepare-commit-msg**
  - タイミング：pre-commit の後、エディタ起動前（デフォルト commit message の準備後）
  - 用途：エディタ起動前の commit message 編集
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | COMMIT_EDITMSG | - | - | - | 
    | 2 | COMMIT_EDITMSG | `message` | - | <ul> <li>`-m` <br> <li> `-F` </ul> |
    | 2 | COMMIT_EDITMSG | `template` | - | <ul> <li>`-t` <br> <li> `commit.template` が指定 </ul> |
    | 2 | COMMIT_EDITMSG | `merge` | - | <ul> <li>マージコミット <br> <li>`.git/MERGE_MSG` が存在</ul> |
    | 2 | COMMIT_EDITMSG | `squash` | - | <ul> <li>`.git/SQUASH_MSG` が存在 </ul>
    | 3 | COMMIT_EDITMSG | `commit` | 参照される commit の SHA1 | - |
- **commit-msg**
  - タイミング：エディタによる編集後
  - 用途：commit message のチェック・編集
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] |  条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | COMMIT_EDITMSG | - | - | - | 
- **post-commit**
  - タイミング：commit の作成完了後
  - 用途：commit 通知
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 0 | - | - | - | - | 
- **applypatch-msg**
  - タイミング：am で commit message が準備できた後
  - 用途：commit message のチェック・編集
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | commit log message のパス | - | - | - | 
- **pre-applypatch**
  - タイミング：am で patch が適用され、commit が作成される前
  - 用途：commit されるファイルのチェック
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 0 | - | - | - | - | 
- **post-applypatch**
  - タイミング：am で commit が作成された後
  - 用途：am の通知
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 0 | - | - | - | - |
- **pre-merge-commit**
  - タイミング：merge で処理が終わって commit message を準備する前
  - 用途：merge の処理チェック？
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 0 | - | - | - | - |
- **post-merge**
  - タイミング：merge で処理が終わった後 (`git pull` の発生タイミング)
  - 用途：worktree の metadata の save/restore?
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | `0` | - | - | `--squash` が未指定な場合 |
    | 1 | `1` | - | - | `--squash` が指定されている場合 |
- **pre-rebase**
  - タイミング：rebase で実処理を行う前
  - 用途：rebase の実行可否チェック
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | upstream | - | - | 現在の branch に対して rebase を行う場合 <br> `git rebase <upstream>` | 
    | 2 | upstream | branch | - | 現在とは違う branch に対して rebase を行う場合 <br> `git rebase <upstream> <branch>` | 
- **post-rewrite**
  - タイミング：`commit --amend` もしくは `rebase` でコミットの書き換えが完了した後
  - 用途：通知とか？
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 1 | `amend` | - | - | `commit --amend` の場合 | 
    | 1 | `rebase` | - | - | `rebase` の場合 | 
- **post-checkout**
  - タイミング： `checkout` / `switch` で worktree を更新した後
  - 用途：通知、差分チェック・差分表示
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 3 | 切り替え前の HEAD の SHA1 | 切り替え後の HEAD の SHA1 | 1 | `checkout <branch>` で branch ごと切り替えた場合 | 
    | 3 | 切り替え前の HEAD の SHA1 | 切り替え後の HEAD の SHA1 | 0 | `checkout <branch> <filename>` で filename だけ切り替えた場合 | 
- **pre-push**
  - タイミング： `push` を行う前
  - 用途：push 可否判定
  - 引数：<br>
    | argc | argv\[1] | argv\[2] | argv\[3] | 条件 |
    |:-:|:-:|:-:|:-:|:-|
    | 2 | remote の名前 | remote URL | - | - |

## 実験
- 実験用リポジトリ
  - https://github.com/uyiromo/git-hooks-sandbox
  - Cline に作らせた

```sh
% git clone https://github.com/uyiromo/git-hooks-sandbox
% cd git-hooks-sandbox
% git config --local core.hooksPath $(realpath hooks)
```

### commit

```sh
% touch $(date +%s)
% git add 1743687059
% git commit
[git-hooks] pre-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] post-commit
  argc: 0
[develop 0e9a3d0] a
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743687059
```

### commit -m 

```sh
% touch $(date +%s)
% git add 1743687094
% git commit -m "msg"
[git-hooks] pre-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: message
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] post-commit
  argc: 0
[develop 48c0bb6] msg
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743687094
```

### commit -F

```sh
% git commit -F commitmsg.txt
[git-hooks] pre-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: message
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] post-commit
  argc: 0
[develop 34d7b1b] test
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743687241
```

### commit -t 

```sh
% touch $(date +%s)
% git add 1743687372
% git commit -t commitmsg.txt
[git-hooks] pre-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: template
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] post-commit
  argc: 0
[develop f53dd09] test hoge
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743687345
 create mode 100644 1743687372
 ```



### commit --amend

```sh
% touch $(date +%s)
% git add 1743687155
% git commit --amend
[git-hooks] pre-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 3
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: commit
  argv[2]: HEAD
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/COMMIT_EDITMSG
[git-hooks] post-commit
  argc: 0
[git-hooks] post-rewrite
  argc: 1
  argv[0]: amend
[develop a8abb2c] msg
 Date: Thu Apr 3 22:31:43 2025 +0900
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743687094
 create mode 100644 1743687155
```

### checkout

```sh
% git rev-parse HEAD
f53dd0993f5031ba69cc4d50b2936d2806d7ed7a
% git rev-parse main
570ae4fe74849d7e6215072dd891c976305915ac
% git checkout main
Switched to branch 'main'
Your branch is ahead of 'origin/main' by 1 commit.
  (use "git push" to publish your local commits)
[git-hooks] post-checkout
  argc: 3
  argv[0]: f53dd0993f5031ba69cc4d50b2936d2806d7ed7a
  argv[1]: 570ae4fe74849d7e6215072dd891c976305915ac
  argv[2]: 1
```



## rebase

```sh
% git log --oneline -n3
      1 07e03ba (HEAD -> test2) add 1743688770
      2 db82513 add 1743688759
      3 10d5d3f add 1743688758

% git rebase -i HEAD~3
[git-hooks] pre-rebase
  argc: 1
  argv[0]: HEAD~3
[git-hooks] post-checkout
  argc: 3
  argv[0]: 07e03baf7324254313ab9e2edab2e2fe4f4d16f7
  argv[1]: 10d5d3fedefb9dbb399d13100a306d3a5d66d394
  argv[2]: 1
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: message
[git-hooks] post-commit
  argc: 0
[git-hooks] post-rewrite
  argc: 1
  argv[0]: amend
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: message
[git-hooks] post-commit
  argc: 0
[git-hooks] post-rewrite
  argc: 1
  argv[0]: amend
[detached HEAD 87257ad] add NEW
 Date: Thu Apr 3 22:59:18 2025 +0900
 4 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743688716
 create mode 100644 1743688758
 create mode 100644 1743688759
 create mode 100644 1743688770
[git-hooks] post-rewrite
  argc: 1
  argv[0]: rebase
Successfully rebased and updated refs/heads/test2.
```

## rebase

```sh
% git rev-parse develop
4a9d4f2d3af91af8167530cd46de8e7202b6a599
% git rev-parse test2
87257ad36cb37ba6e26b8084e76853cd657ca514
% git rebase develop test2
[git-hooks] pre-rebase
  argc: 2
  argv[0]: develop
  argv[1]: test2
[git-hooks] post-checkout
  argc: 3
  argv[0]: 570ae4fe74849d7e6215072dd891c976305915ac
  argv[1]: 4a9d4f2d3af91af8167530cd46de8e7202b6a599
  argv[2]: 1
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/COMMIT_EDITMSG
  argv[1]: message
[git-hooks] post-commit
  argc: 0
[git-hooks] post-rewrite
  argc: 1
  argv[0]: rebase
Successfully rebased and updated refs/heads/test2.
```

### am

```sh
% git am 0001-add-NEW.patch
[git-hooks] applypatch-msg
  argc: 1
  argv[0]: .git/rebase-apply/final-commit
Applying: add NEW
[git-hooks] pre-applypatch
  argc: 0
[git-hooks] post-applypatch
  argc: 0
```

### push

```sh
% git push -u origin test2
[git-hooks] pre-push
  argc: 2
  argv[0]: origin
  argv[1]: git@github.com:uyiromo/git-hooks-sandbox.git
Enumerating objects: 13, done.
Counting objects: 100% (13/13), done.
Delta compression using up to 4 threads
Compressing objects: 100% (12/12), done.
Writing objects: 100% (12/12), 1.19 KiB | 610.00 KiB/s, done.
Total 12 (delta 5), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (5/5), done.
To github.com:uyiromo/git-hooks-sandbox.git
   f7413f5..f1d20f2  test2 -> test2
branch 'test2' set up to track 'origin/test2'.
```

### merge --no-ff

```sh
% git merge develop2 --no-ff
[git-hooks] pre-merge-commit
  argc: 0
[git-hooks] prepare-commit-msg
  argc: 2
  argv[0]: .git/MERGE_MSG
  argv[1]: merge
[git-hooks] commit-msg
  argc: 1
  argv[0]: .git/MERGE_MSG
Merge made by the 'ort' strategy.
 1743689238 | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743689238
[git-hooks] post-merge
  argc: 1
  argv[0]: 0
```


### merge --ff

```sh
% git merge develop3 --ff
Updating 5dcfbd1..ae8acb4
Fast-forward
 1743689756 | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743689756
[git-hooks] post-merge
  argc: 1
  argv[0]: 0
```

### merge --squash

```sh
% git merge develop4 --squash --ff
Updating ae8acb4..04e5643
Fast-forward
Squash commit -- not updating HEAD
 1743689798 | 0
 1743689799 | 0
 2 files changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 1743689798
 create mode 100644 1743689799
[git-hooks] post-merge
  argc: 1
  argv[0]: 1
```