個人的に意識しているGitリポジトリの初期設定

## 結論

- `git init`直後のコミットは空コミットにしておいたほうがいいのでしている
- EditorConfigでファイルフォーマットを統一するようにしている
- Gitフックでコミット先のブランチを間違えないようにしている
- GitHubなどのGitホスティングサービス側で特定ブランチのpushを受け付けないようにしている

## はじめに
:::note
すべて個人の感想です
:::

何か開発を行うとき、バージョン管理システムとしてGitを使うことが多いと思います。
しかし、Gitを使った開発を行っていると、以下のようなことが起きてしまうことがあります。

- 開発者のエディタ間の設定が異なっていてタブとスペースが混在する
- ブランチを切り替え忘れて本来コミットしてはいけない`main`ブランチなどにコミットしてしまう

これらを防ぐよう、個人的に意識しているGitリポジトリの初期設定をまとめようと思います。

#### 対象読者

- Git操作(`add`, `commit`, `push`, `rebase`など)が分かる人
- チーム開発を行うときに意図しない変更を防ぎたい人
- Gitを活用したい人

#### 執筆にあたっての実行環境
- OS: macOS 15.1.1 24B91 arm64
- Gitバージョン: 2.39.5 (Apple Git-154)

## 1. 最初のコミットは空コミットにする

`git init`を実行した直後に作成するコミットは**ルートコミット**と呼ばれる特殊なコミットです。
ルートコミットはコミット情報に親コミット(1つ前のコミット情報)を持たないため、rebaseなどの操作が行いにくいです。
そこで`--allow-empty`オプションを付け、空のコミットを作ることでrebaseなどの操作を行いやすくします。

```bash
$ git init # リポジトリの作成
$ git commit -m "feat: create new repository" --allow-empty # 空コミットの作成
[main (root-commit) 85a3ead] feat: create new repository
$ git commit -m "chore: 2nd commit" --allow-empty # 2つ目のコミット
[main 0b34365] chore: 2nd commit
$ git log --pretty=raw # コミット全体の情報を表示
commit 0b343658475dd01cb9d15a28d98951a33adeb3be
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904
parent 85a3eadeb80e442f749bbbd2bfdd4e527258eab5 # parentがある
author Mitsu <tech.mitsu.yuki@gmail.com> 1740490213 +0900
committer Mitsu <tech.mitsu.yuki@gmail.com> 1740490213 +0900

    chore: 2nd commit

commit 85a3eadeb80e442f749bbbd2bfdd4e527258eab5
tree 4b825dc642cb6eb9a060e54bf8d69288fbee4904 # parentがない
author Mitsu <tech.mitsu.yuki@gmail.com> 1740490205 +0900
committer Mitsu <tech.mitsu.yuki@gmail.com> 1740490205 +0900

    feat: create new repository
```

::: note
個人的にはまだ初回でrebaseを使うような場面には出会ったことは無いです。
ですが、いつ出会うのかわからないのでやるようにはしています。
:::

## 2. `README.md`をコミットするよりも先に`.editorconfig`を設定する

Gitを使うということは何かしらのテキストファイルを管理することになると思います。言ってしまえば`README.md`もテキストファイルです。
これらのテキストファイルのタブインデントといったフォーマットを、エディタに依らず設定できるものがEditorConfigです。

https://editorconfig.org/

EditorConfigに対応しているエディタや編集環境は非常に多いです。
エディタ単体で対応しているものでいうとVimやNeovim、IntelliJ IDEAなどのJetBrains系。GitHubやGitLabなどのGitホスティングサービス側のオンラインエディタで対応していたりします。
別途プラグインが必要にはなりますが、EmacsやSublime Text、VSCode、Notepad++などが対応しています。

これだけ多くのエディタがサポートしている、ファイルフォーマット設定を行わないわけにはいかないですね。

設定ファイルの記述も簡単なのでサクッと書いちゃいましょう。
以下は私が作成したGo言語プロジェクト向けの`.editorconfig`です。

```:.editorconfig
root = true

[*]
end_of_line = lf
insert_final_newline = true
charset = utf-8
indent_style = space
indent_size = 2
trim_trailing_whitespace = true

[*.go]
indent_style = tab
indent_size = 4

[*.md]
trim_trailing_whitespace = false
```

これを導入することで以下のようなファイルフォーマット設定が有効になります
- 改行コードはLF
- ファイル末尾の空行を許可
- 文字コードはUTF-8
- インデントは基本的にスペース幅が2のスペースインデント
- goファイルのインデントはスペース幅が4のタブインデント
- Markdownファイルは行末スペースを許可

これでセーブした途端にエディタのフォーマッターが実行されて、インデントが狂うことや、Markdownファイルの末尾スペースが削除されて改行が消滅する悲劇から救われますね。

## 3. Gitフックを使って大事なブランチへの誤コミットを防ぐ

作業に没頭しているとブランチを切り替え忘れ、`main`などの大事なブランチに誤ってコミットしてしまうことがあると思います。これを防ぐ方法の一つがGitフックです。
Gitフックとは、Gitの特定のアクションが発生したときにカスタムスクリプトを実行する機能です。

このカスタムスクリプトは`git init`時にリポジトリの`.git/hooks`に作成されます。
初期状態では`.git/hooks`以下にサンプルスクリプトが作成されています。

:::note info
`git init`時に作成されますが、ファイル名の末尾が`.sample`になっているとカスタムスクリプトは実行されません。
実際に使うときは`.sample`を除去したファイル名にします。
:::

```bash
$ ls .git/hooks
applypatch-msg.sample     pre-push.sample
commit-msg.sample         pre-rebase.sample
fsmonitor-watchman.sample pre-receive.sample
post-update.sample        prepare-commit-msg.sample
pre-applypatch.sample     push-to-checkout.sample
pre-commit.sample         update.sample
pre-merge-commit.sample
```
例えば`git push`を行う前に`pre-push`というシェルスクリプトが実行されるような仕組みです。
このカスタムスクリプトの中に、コミットする前に実行する`pre-commit.sample`とあります。
こちらに簡単なシェルスクリプトを作成して、特定のブランチ名のときはコミットできないようなシェルスクリプトを作成しましょう。
```bash:.git/hooks/pre-commit
#/usr/bin/env bash

current_branch=$(git rev-parse --abbrev-ref HEAD)

# コミットを禁止するブランチ名を指定する
declare -a forbidden_branches=("release" "main")

for branch in "${forbidden_branches[@]}"; do
  if [ "${current_branch}" = "${branch}" ]; then
    echo "❌ Error: You are trying to commit directly to ${branch}!"
    echo "Please create a new branch and commit there instead. 🌟"
    exit 1
  fi
done
```

作成したら`.git/hooks/pre-commit`に実行権限を付けましょう。

```bash
$ chmod u+x .git/hooks/pre-commit
```

`main`ブランチにコミットしてみると`pre-commit`が実行され、コミットが作られていないことがわかります。

```bash
$ git commit -m "wrong commit" --allow-empty
❌ Error: You are trying to commit directly to main!
Please create a new branch and commit there instead. 🌟

$ git log --oneline
0b34365 (HEAD -> main) chore: 2nd commit
85a3ead feat: create new repository
```

### GitフックをGitで管理して共有する
Gitフックの便利さが伝わったと思います。
しかし作成したGitフックのカスタムスクリプトは`.git`ディレクトリ以下にあるため、そのままではGitで管理できず、プロジェクトとしての共有もできません。
そこでGitで管理できる別のフォルダにカスタムスクリプトを作成し、Gitフックが保存されいてるディレクトリ設定を変更します。

#### カスタムスクリプトを格納するディレクトリを作る
場所はどこでも良いのですが、個人的には`.githooks`が気に入っています

```bash
$ mkdir .githooks
$ vim .githooks/pre-commit # pre-commitを編集
$ chmod u+x .githooks/pre-commit # 実行権限付与を忘れずに
$ tree
.
└── .githooks
    └── pre-commit
```

これで作成したカスタムスクリプトをGitで管理することができました。

#### カスタムスクリプトを共有する

実行するGitフックのカスタムスクリプトが置いてあるパスは、デフォルトでは`.git/hooks`を使用します。
これはGitの設定にある`core.hooksPath`より変更が可能です。
先ほど作成した`.githooks`を指定する場合は以下のコマンドを実行します。

```bash
$ git config --local core.hooksPath .githooks # Gitフックの参照先パスを.githooksにする

$ git config -l --local
(省略)
core.hooksPath=.githooks # 参照先が.githooksになっている
```

こうすることでGitフックのカスタムスクリプトをGitで管理しつつ、プロジェクトで共有することができるようになりました。

あとは`README.md`などに「`git config --local core.hooksPath .githooks`を実行しておいてね」とか記述しておけば良いと思います。

## 4. GitHubで保護ブランチを設定する

Gitフックで大事なブランチへの誤コミットを防ぐ仕組みを作っていても、Gitフックの設定をしていない人がいたら元も子もありません。
そこで、間違って大事なブランチへの誤コミットされた上にpushされても、GitHubからpushを受け入れないようにしましょう。

1. 設定したいGitHubのリポジトリに移動して「Settings」を選択します
1. サイドバーの「Code and automation」から「Branches」を選択します
1. 「Branch protection rules」の横にある「Add rule」を選択します
![スクリーンショット 2025-02-25 23.49.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320986/bcf0f299-6b40-4a85-965a-43e32e8c4e31.png)

4. 「Branch name pattern」に保護したいブランチ名を入力します
4. 「Lock Branch」にチェックを入れます
![スクリーンショット 2025-02-26 0.03.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/320986/c9c2055d-fe21-4a86-9721-2dd03334491f.png)

これにて大事なブランチにpushされてしまい悲劇から回避できます。

## まとめ

個人的に意識しているGitリポジトリの初期設定について説明しました。
作業ミスや不注意による事故を仕組みで減らせるなら減らしていきたいものですｎ。
誰かの助けになれば幸いです。

## 参考にしたページ

https://qiita.com/NorsteinBekkler/items/b2418cd5e14a52189d19

https://git-scm.com/docs/githooks

https://zenn.dev/dzeyelid/articles/b8309a130d3b3b