Git 設定
Git の初期設定からGPG署名・SSH鍵・diff/mergeツール連携まで、プロフェッショナルなGit環境を構築するための完全ガイド。
Git 設定
Git の初期設定からGPG署名・SSH鍵・diff/mergeツール連携まで、プロフェッショナルなGit環境を構築するための完全ガイド。
この章で学ぶこと
.gitconfigの体系的な設定と実用的なエイリアス構築- GPG 署名と SSH 鍵の正しい設定方法
- diff/merge ツールの統合と credential helper の管理
- 複数アカウントの使い分けと Git hooks の活用
- トラブルシューティングとパフォーマンス最適化
前提知識
このガイドを読む前に、以下の知識があると理解が深まります:
- 基本的なプログラミングの知識
- 関連する基礎概念の理解
- ターミナル設定 の内容を理解していること
1. Git の初期設定
1.1 基本設定
# ユーザー情報
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
# デフォルトブランチ名
git config --global init.defaultBranch main
# エディタ設定
git config --global core.editor "code --wait"
# 改行コード処理
# macOS/Linux
git config --global core.autocrlf input
# Windows
git config --global core.autocrlf true
# 日本語ファイル名の表示
git config --global core.quotepath false
# カラー出力
git config --global color.ui auto
# ページャー設定 (delta 推奨)
git config --global core.pager "delta"
# 大文字小文字を区別
git config --global core.ignorecase false
# シンボリックリンクを追跡
git config --global core.symlinks true1.2 設定ファイルの階層
Git 設定の優先順位 (高い順):| 1. Local (.git/config) |
|---|
| → リポジトリ固有の設定 |
| → git config --local |
| → 例: プロジェクト固有の user.email |
| 2. Global (~/.gitconfig) |
| → ユーザー共通の設定 |
| → git config --global |
| → 例: 個人のエイリアス、エディタ設定 |
| 3. System (/etc/gitconfig) |
| → システム全体の設定 |
| → git config --system |
| → 例: 組織のプロキシ設定 |
| 4. Portable (/path/to/.gitconfig) |
| → GIT_CONFIG_GLOBAL 環境変数で指定 |
| → dotfiles リポジトリからの共有設定 |
※ 同じキーが複数レベルで設定された場合、
上位(Local)の値が優先される
1.3 設定の確認と管理
# 全設定の一覧表示 (適用元も表示)
git config --list --show-origin
# 特定のキーの値を確認
git config user.email
# 設定の適用元を確認
git config --show-origin user.email
# 設定のスコープを確認
git config --show-scope --list
# 設定を削除
git config --global --unset user.signingkey
# 設定ファイルを直接編集
git config --global --edit
# 条件付き設定の確認
git config --list --show-origin | grep includeIf2. .gitconfig 完全版
2.1 推奨設定
# ~/.gitconfig
[user]
name = Your Name
email = your.email@example.com
signingkey = YOUR_GPG_KEY_ID
[core]
editor = code --wait
autocrlf = input
quotepath = false
pager = delta
ignorecase = false
whitespace = trailing-space,space-before-tab
precomposeunicode = true
fsmonitor = true
untrackedcache = true
symlinks = true
# 大規模リポジトリでのパフォーマンス改善
# fsmonitor = true # Git 2.37+ (Watchman or built-in)
[init]
defaultBranch = main
[color]
ui = auto
[color "branch"]
current = yellow reverse
local = yellow
remote = green
[color "status"]
added = green
changed = yellow
untracked = red
[push]
default = current
autoSetupRemote = true
followTags = true
# Git 2.40+: push 時に新しいブランチも自動設定
# useForceIfIncludes = true # force-with-lease の強化版
[pull]
rebase = true
[fetch]
prune = true
prunetags = true
# 並列フェッチ (Git 2.36+)
parallel = 0 # CPUコア数に自動設定
writeCommitGraph = true
[merge]
conflictstyle = zdiff3
tool = vscode
# マージコミットに対象ブランチのログを含める
log = 20
[mergetool]
keepBackup = false
prompt = false
[mergetool "vscode"]
cmd = code --wait --merge $REMOTE $LOCAL $BASE $MERGED
[diff]
tool = vscode
colorMoved = default
algorithm = histogram
# バイナリファイルの差分表示
# wordRegex = . # 単語単位の差分
renames = copies
mnemonicPrefix = true
submodule = log
[difftool "vscode"]
cmd = code --wait --diff $LOCAL $REMOTE
[difftool]
prompt = false
[rebase]
autosquash = true
autostash = true
# rebase 時のコンフリクト検出を改善
updateRefs = true
# instructionFormat = "%s [%an, %ar]"
[rerere]
enabled = true
# rerere の自動ステージング
autoupdate = true
[commit]
gpgsign = true
verbose = true
# コミットメッセージテンプレート
# template = ~/.gitmessage
[tag]
gpgsign = true
sort = version:refname
[interactive]
diffFilter = delta --color-only
[delta]
navigate = true
light = false
side-by-side = true
line-numbers = true
syntax-theme = Dracula
file-style = bold yellow ul
file-decoration-style = none
hunk-header-decoration-style = cyan box ul
minus-style = syntax "#3f0001"
plus-style = syntax "#003800"
line-numbers-minus-style = "#ff0000"
line-numbers-plus-style = "#00ff00"
map-styles = "bold purple => syntax #330033, bold cyan => syntax #003333"
[branch]
sort = -committerdate
# 新しいブランチ作成時に自動でリモート追跡
autoSetupMerge = always
[column]
ui = auto
[help]
autocorrect = prompt
# autocorrect = 10 # 1秒後に自動修正実行
[log]
date = iso
# abbrevCommit = true
# follow = true # ファイル名変更を自動追跡
[status]
showUntrackedFiles = all
submoduleSummary = true
# short = true # デフォルトで短縮形式
[stash]
showPatch = true
[transfer]
# Git オブジェクトの整合性チェック
fsckObjects = true
[receive]
fsckObjects = true
[url "git@github.com:"]
insteadOf = https://github.com/
# GitHub CLI との統合
[credential "https://github.com"]
helper = !/usr/bin/gh auth git-credential
# Git LFS
[filter "lfs"]
clean = git-lfs clean -- %f
smudge = git-lfs smudge -- %f
process = git-lfs filter-process
required = true2.2 コミットメッセージテンプレート
# テンプレートファイルを作成
cat << 'EOF' > ~/.gitmessage
# <type>(<scope>): <subject>
#
# <body>
#
# <footer>
#
# ─── Type ───────────────────────────
# feat: 新機能
# fix: バグ修正
# docs: ドキュメント変更
# style: コードスタイル変更 (動作に影響しない)
# refactor: リファクタリング
# perf: パフォーマンス改善
# test: テスト追加・修正
# build: ビルドシステム変更
# ci: CI 設定変更
# chore: その他の変更
# revert: コミットの取り消し
#
# ─── Rules ──────────────────────────
# Subject: 50文字以内、命令形、ピリオド不要
# Body: 72文字で折り返し、何を・なぜ変えたか
# Footer: Breaking Changes, Issue参照
#
# ─── Example ────────────────────────
# feat(auth): add JWT token refresh mechanism
#
# Implement automatic token refresh when the access
# token expires. The refresh token is stored securely
# in an HTTP-only cookie.
#
# Closes #123
# BREAKING CHANGE: API now requires Authorization header
EOF
# テンプレートを有効化
git config --global commit.template ~/.gitmessage3. エイリアス
3.1 実用的エイリアス集
# ~/.gitconfig の [alias] セクション
[alias]
# ─── 基本操作の短縮 ───
s = status
a = add
aa = add --all
ap = add --patch
c = commit
cm = commit -m
ca = commit --amend
can = commit --amend --no-edit
co = checkout
sw = switch
sc = switch -c
br = branch
d = diff
ds = diff --staged
dw = diff --word-diff
p = push
pf = push --force-with-lease
pl = pull
f = fetch --all --prune
m = merge
rb = rebase
rbi = rebase -i
cp = cherry-pick
st = stash
stp = stash pop
stl = stash list
# ─── ログ表示 ───
lg = log --oneline --graph --decorate --all -20
ll = log --pretty=format:'%C(yellow)%h%C(reset) %C(green)(%cr)%C(reset) %s %C(bold blue)<%an>%C(reset)%C(red)%d%C(reset)' --abbrev-commit -20
hist = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --all
lp = log --patch -5
ls = log --stat --oneline -10
# ─── ファイル変更の追跡 ───
filelog = log --follow -p --
blame-line = "!f() { git log -L $1,$1:$2; }; f"
contributors = shortlog --summary --numbered --email
# ─── 便利コマンド ───
# 直前のコミットを修正 (メッセージそのまま)
amend = commit --amend --no-edit
# ステージング取り消し
unstage = restore --staged
# 最後のコミットを取り消し (変更は保持)
undo = reset --soft HEAD~1
# 変更を一時退避
stash-all = stash push --include-untracked -m
# ブランチを最新に更新
sync = !git fetch --all --prune && git pull --rebase
# マージ済みブランチを削除
cleanup = !git branch --merged | grep -v '\\*\\|main\\|master\\|develop' | xargs -n 1 git branch -d
# リモートのマージ済みブランチも削除
cleanup-remote = !git fetch --prune && git branch -r --merged origin/main | grep -v 'main\\|develop' | sed 's/origin\\///' | xargs -n 1 git push origin --delete
# ファイルの変更履歴
history = log --follow -p --
# 今日のコミット
today = log --since='midnight' --oneline --author='Your Name'
# 今週のコミット
week = log --since='1 week ago' --oneline --author='Your Name'
# WIP コミット
wip = !git add -A && git commit -m 'WIP: work in progress [skip ci]'
# WIP 取り消し
unwip = !git log -1 --format='%s' | grep -q 'WIP' && git reset HEAD~1
# 初期コミット (空)
init-commit = !git init && git commit --allow-empty -m 'chore: initial commit'
# ブランチ名をコピー
branch-name = rev-parse --abbrev-ref HEAD
# 最近のブランチ一覧
recent = branch --sort=-committerdate --format='%(committerdate:relative)\t%(refname:short)\t%(subject)' -20
# 差分の統計
stat = diff --stat
# コミット間の差分ファイル一覧
changed = diff --name-only
# コンフリクトファイル一覧
conflicts = diff --name-only --diff-filter=U
# Git root ディレクトリ表示
root = rev-parse --show-toplevel
# fixup コミット (autosquash 用)
fixup = "!f() { git commit --fixup=$1; }; f"
# タグ一覧 (バージョン順)
tags = tag -l --sort=-version:refname
# リモート URL 表示
remote-url = remote get-url origin
# ブランチ比較 (何コミット差があるか)
ahead = "!f() { git rev-list --count HEAD..${1:-origin/main}; }; f"
behind = "!f() { git rev-list --count ${1:-origin/main}..HEAD; }; f"
# インタラクティブな add (パッチモード)
patch = add --patch
# すべての変更を取り消し (注意して使用)
nuke = !git reset --hard HEAD && git clean -fd
# ローカルの main を最新に
update-main = !git checkout main && git pull && git checkout -
# PR 用: 現在のブランチの全コミットを表示
pr-log = "!git log --oneline $(git merge-base HEAD main)..HEAD"
# PR 用: 現在のブランチの全変更ファイルを表示
pr-files = "!git diff --name-only $(git merge-base HEAD main)..HEAD"
# PR 用: 現在のブランチの差分統計
pr-stat = "!git diff --stat $(git merge-base HEAD main)..HEAD"3.2 エイリアスの動作
git lg の出力例:
* a1b2c3d (HEAD -> feature/auth, origin/feature/auth) Add JWT middleware
* d4e5f6g Add login endpoint
* g7h8i9j Add user model
| * j0k1l2m (origin/feature/ui) Update dashboard layout
| * m3n4o5p Add sidebar component
|/
* p6q7r8s (origin/main, main) Release v1.2.0
* s9t0u1v Fix database connection pool
* v2w3x4y Add health check endpoint
git recent の出力例:
2 hours ago feature/auth Add JWT middleware
5 hours ago feature/ui Update dashboard layout
1 day ago fix/db-pool Fix connection pool leak
3 days ago main Release v1.2.0
1 week ago feature/search Add full-text search
git pr-stat の出力例:
src/middleware/auth.ts | 45 ++++++++++++
src/routes/login.ts | 32 +++++++++
src/models/user.ts | 28 +++++++++
tests/auth.test.ts | 67 ++++++++++++++++
4 files changed, 172 insertions(+)
4. SSH 鍵の設定
4.1 Ed25519 鍵の生成
# SSH 鍵を生成 (Ed25519 推奨)
ssh-keygen -t ed25519 -C "your.email@example.com"
# 鍵の保存場所 (デフォルト: ~/.ssh/id_ed25519)
# パスフレーズを設定 (空にしない)
# SSH エージェントに鍵を登録
eval "$(ssh-agent -s)"
# macOS: キーチェーンに保存
ssh-add --apple-use-keychain ~/.ssh/id_ed25519
# Linux
ssh-add ~/.ssh/id_ed25519
# 公開鍵をコピーして GitHub に登録
cat ~/.ssh/id_ed25519.pub | pbcopy # macOS
cat ~/.ssh/id_ed25519.pub | xclip -selection clipboard # Linux
# GitHub → Settings → SSH and GPG keys → New SSH key
# ─── 鍵の種類比較 ───
# Ed25519: 推奨。高速、安全、鍵サイズが小さい
# RSA 4096: レガシー互換。古いシステムとの接続に必要な場合
# ECDSA: 非推奨。NSA関与の疑念あり4.2 SSH config
# ~/.ssh/config
# ─── GitHub (個人) ───
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519
AddKeysToAgent yes
UseKeychain yes # macOS のみ
IdentitiesOnly yes
# ─── GitHub (会社用アカウント) ───
Host github-work
HostName github.com
User git
IdentityFile ~/.ssh/id_ed25519_work
AddKeysToAgent yes
IdentitiesOnly yes
# ─── GitLab ───
Host gitlab.com
HostName gitlab.com
User git
IdentityFile ~/.ssh/id_ed25519
PreferredAuthentications publickey
# ─── Bitbucket ───
Host bitbucket.org
HostName bitbucket.org
User git
IdentityFile ~/.ssh/id_ed25519
# ─── 自社 Git サーバー ───
Host git.company.com
HostName git.company.com
User git
Port 2222
IdentityFile ~/.ssh/id_ed25519_work
ProxyCommand ssh -q -W %h:%p bastion.company.com
# ─── 踏み台サーバー経由 ───
Host bastion.company.com
HostName bastion.company.com
User your-username
IdentityFile ~/.ssh/id_ed25519_work
# ─── 全ホスト共通設定 ───
Host *
ServerAliveInterval 60
ServerAliveCountMax 3
AddKeysToAgent yes
Compression yes
# 使い分け:
# git clone git@github.com:personal/repo.git # 個人
# git clone git@github-work:company/repo.git # 会社
# git clone git@gitlab.com:team/repo.git # GitLab4.3 SSH 鍵のセキュリティベストプラクティス
# ─── 鍵のパーミッション確認 ───
ls -la ~/.ssh/
# 正しいパーミッション:
# drwx------ ~/.ssh/ (700)
# -rw------- id_ed25519 (600)
# -rw-r--r-- id_ed25519.pub (644)
# -rw------- config (600)
# パーミッション修正
chmod 700 ~/.ssh
chmod 600 ~/.ssh/id_ed25519
chmod 644 ~/.ssh/id_ed25519.pub
chmod 600 ~/.ssh/config
# ─── 鍵のフィンガープリント確認 ───
ssh-keygen -lf ~/.ssh/id_ed25519.pub
# 256 SHA256:xxxxx your.email@example.com (ED25519)
# ─── 複数鍵の管理 ───
# ssh-add で登録済み鍵を確認
ssh-add -l
# 特定の鍵を削除
ssh-add -d ~/.ssh/id_ed25519_old
# 全鍵を削除
ssh-add -D
# ─── 鍵のローテーション ───
# 1. 新しい鍵を生成
ssh-keygen -t ed25519 -C "your.email@example.com" -f ~/.ssh/id_ed25519_new
# 2. GitHub に新しい公開鍵を追加
# 3. SSH config を更新
# 4. テスト
ssh -T git@github.com
# 5. 古い公開鍵を GitHub から削除
# 6. 古い鍵ファイルを削除4.4 接続テスト
# GitHub
ssh -T git@github.com
# Hi username! You've successfully authenticated, but GitHub
# does not provide shell access.
# GitLab
ssh -T git@gitlab.com
# Welcome to GitLab, @username!
# 詳細デバッグ
ssh -vT git@github.com
# デバッグ出力で認証プロセスを確認
# 会社用アカウントのテスト
ssh -T git@github-work5. GPG 署名
5.1 GPG 鍵の生成と設定
# GPG 鍵を生成
gpg --full-generate-key
# 種類: RSA and RSA (default)
# 鍵長: 4096
# 有効期限: 2y (推奨)
# 名前とメールアドレス: Git と同じものを入力
# 鍵 ID を確認
gpg --list-secret-keys --keyid-format=long
# sec rsa4096/3AA5C34371567BD2 2024-01-01 [SC] [expires: 2026-01-01]
# ABCDEF1234567890ABCDEF1234567890ABCDEF12
# uid [ultimate] Your Name <your.email@example.com>
# ssb rsa4096/42B317FD4BA89E7A 2024-01-01 [E] [expires: 2026-01-01]
# 鍵 ID: 3AA5C34371567BD2
# Git に設定
git config --global user.signingkey 3AA5C34371567BD2
git config --global commit.gpgsign true
git config --global tag.gpgsign true
# 公開鍵をエクスポートして GitHub に登録
gpg --armor --export 3AA5C34371567BD2 | pbcopy
# GitHub → Settings → SSH and GPG keys → New GPG key
# macOS: pinentry-mac でパスフレーズ入力を GUI 化
brew install pinentry-mac
echo "pinentry-program $(which pinentry-mac)" >> ~/.gnupg/gpg-agent.conf
gpgconf --kill gpg-agent
# ─── GPG Agent の設定 ───
# ~/.gnupg/gpg-agent.conf
cat << 'EOF' > ~/.gnupg/gpg-agent.conf
# キャッシュ時間 (秒)
default-cache-ttl 28800 # 8時間
max-cache-ttl 86400 # 24時間
# macOS
pinentry-program /opt/homebrew/bin/pinentry-mac
# Linux (GUI)
# pinentry-program /usr/bin/pinentry-gnome3
# Linux (CUI)
# pinentry-program /usr/bin/pinentry-tty
EOF
# GPG Agent を再起動
gpgconf --kill gpg-agent
gpg-agent --daemon5.2 SSH 鍵による署名 (GPG の代替)
# Git 2.34+ で SSH 鍵を使った署名が可能
# GPG のセットアップが面倒な場合の代替手段
# SSH 鍵で署名する設定
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
# 署名の検証に使う公開鍵リスト
git config --global gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers
# ~/.ssh/allowed_signers
# メールアドレスと公開鍵のマッピング
cat << 'EOF' > ~/.ssh/allowed_signers
your.email@example.com ssh-ed25519 AAAA... your.email@example.com
colleague@example.com ssh-ed25519 BBBB... colleague@example.com
EOF
# 署名の検証
git log --show-signature -1
git verify-commit HEAD
git verify-tag v1.0.0
# GPG vs SSH 署名の比較:
# GPG: 業界標準、GitHub "Verified" バッジ対応、設定が複雑
# SSH: 設定が簡単、Git 2.34+ 必須、GitHub 対応済み5.3 署名の検証フロー
GPG 署名付きコミットのフロー:
開発者の環境 GitHub| git commit | |||
|---|---|---|---|
| 公開鍵を | |||
| ▼ | 登録済み | ||
| GPG/SSH 秘密鍵で | push | ||
| 署名を付与 | ───────→ | ▼ | |
| 公開鍵で | |||
| ▼ | 署名を検証 | ||
| 署名付き | |||
| コミット | ▼ | ||
| 完成 | ✅ Verified |
Vigilant Mode 有効時: ┌──────────────────┐
│ 署名なしコミット │ → ⚠️ Unverified 表示
│ 署名あり (有効) │ → ✅ Verified 表示
│ 署名あり (無効) │ → ❌ Invalid 表示
└──────────────────┘
6. Credential Helper
6.1 プラットフォーム別設定
# macOS (Keychain)
git config --global credential.helper osxkeychain
# Windows (Credential Manager)
git config --global credential.helper manager
# Linux (libsecret)
sudo apt install libsecret-1-0 libsecret-1-dev
sudo make --directory=/usr/share/doc/git/contrib/credential/libsecret
git config --global credential.helper /usr/share/doc/git/contrib/credential/libsecret/git-credential-libsecret
# Linux (GNOME Keyring)
git config --global credential.helper /usr/lib/git-core/git-credential-libsecret
# GitHub CLI (推奨: 全プラットフォーム共通)
gh auth login
gh auth setup-git
git config --global credential.helper '!gh auth git-credential'
# キャッシュ (一時的、サーバー向け)
git config --global credential.helper 'cache --timeout=3600'6.2 認証方式比較
| 方式 | セキュリティ | 利便性 | 推奨度 | 備考 |
|---|---|---|---|---|
| SSH 鍵 | 高 | 高 | 最推奨 | パスフレーズ + Agent |
| gh auth (CLI) | 高 | 高 | 推奨 | OAuth ベース |
| SSH 署名 | 高 | 高 | 推奨 | Git 2.34+ |
| Personal Access Token | 中 | 中 | 可 | スコープ制限必須 |
| Fine-grained PAT | 高 | 中 | 推奨 | リポジトリ単位制限 |
| パスワード認証 | 低 | - | 廃止済み | 2021年8月廃止 |
6.3 Personal Access Token の管理
# Fine-grained Personal Access Token の作成手順:
# 1. GitHub → Settings → Developer settings
# 2. Personal access tokens → Fine-grained tokens
# 3. Generate new token
# 4. 設定:
# - Token name: 用途を明記 (e.g., "CI/CD pipeline")
# - Expiration: 最大90日推奨
# - Repository access: Only select repositories
# - Permissions: 最小権限の原則
# - Contents: Read-only (クローンのみ)
# - Contents: Read and write (push も必要な場合)
# - Pull requests: Read and write (PR 操作)
# トークンの保存 (macOS)
# Keychain Access に自動保存される
# トークンの更新
# 期限切れ時に再認証:
gh auth refresh
# または: Credential Manager から古いエントリを削除7. diff / merge ツール
7.1 delta の設定
# delta インストール
brew install git-delta # macOS
sudo apt install git-delta # Ubuntu
# ~/.gitconfig で設定 (前述の [delta] セクション参照)
# 使用例
git diff # 自動で delta が適用される
git log -p # パッチ表示も delta で美麗に
git show HEAD # コミット詳細も delta 対応
git stash show -p # stash の差分も delta で表示
git blame file.ts # blame も delta 対応
# delta の表示モード切替
# サイドバイサイド (デフォルト設定)
git diff
# 統合表示 (一時的に変更)
git -c delta.side-by-side=false diff
# 行番号なし
git -c delta.line-numbers=false diff7.2 delta のテーマカスタマイズ
# ~/.gitconfig の [delta] セクション (詳細版)
[delta]
navigate = true
light = false
side-by-side = true
line-numbers = true
syntax-theme = Catppuccin Mocha
# ファイルヘッダー
file-style = bold yellow ul
file-decoration-style = none
file-added-label = [+]
file-modified-label = [~]
file-removed-label = [-]
file-renamed-label = [→]
# ハンクヘッダー
hunk-header-decoration-style = cyan box ul
hunk-header-style = file line-number syntax
# 差分表示
minus-style = syntax "#3B1219"
minus-emph-style = syntax "#6F1223"
plus-style = syntax "#1A2B1A"
plus-emph-style = syntax "#2B4B2B"
# 行番号
line-numbers-minus-style = "#F38BA8"
line-numbers-plus-style = "#A6E3A1"
line-numbers-zero-style = "#585B70"
line-numbers-left-format = "{nm:>4} "
line-numbers-right-format = "{np:>4} │ "
# blame
blame-palette = "#1e1e2e #181825 #11111b #313244 #45475a"
blame-format = "{author:<18} {commit:<8} {timestamp:<16}"
# merge conflict
merge-conflict-begin-symbol = ▼
merge-conflict-end-symbol = ▲
merge-conflict-ours-diff-header-style = yellow bold
merge-conflict-theirs-diff-header-style = cyan bold
# インライン差分 (単語単位)
inline-hint-style = syntax7.3 VS Code をdiff/mergeツールに設定
# diff ツール
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'
git config --global difftool.prompt false
# merge ツール
git config --global merge.tool vscode
git config --global mergetool.vscode.cmd 'code --wait --merge $REMOTE $LOCAL $BASE $MERGED'
git config --global mergetool.keepBackup false
git config --global mergetool.prompt false
# 使用方法
git difftool # VS Code で差分表示
git difftool --dir-diff # ディレクトリ単位の差分表示
git mergetool # VS Code でコンフリクト解決
# IntelliJ IDEA を使う場合
git config --global diff.tool intellij
git config --global difftool.intellij.cmd 'idea diff $LOCAL $REMOTE'
git config --global merge.tool intellij
git config --global mergetool.intellij.cmd 'idea merge $LOCAL $REMOTE $BASE $MERGED'7.4 コンフリクト解決のフロー
zdiff3 形式のコンフリクト表示:
<<<<<<< HEAD (現在のブランチ)
const timeout = 5000;
||||||| parent of abc1234 (共通祖先)
const timeout = 3000;
=======
const timeout = 10000;
>>>>>>> feature/update-config (マージ元)
zdiff3 の利点:
- 共通祖先 (|||||||) が表示される
- 「何から何に変えたか」が一目瞭然
- 標準の diff3 より見やすい
コンフリクト解決のステップ:
1. git mergetool で VS Code を開く
2. 3ウェイマージビューで変更を比較
3. 「Accept Current」「Accept Incoming」「Accept Both」から選択
4. 必要に応じて手動編集
5. 保存してエディタを閉じる
6. git add <resolved-file>
7. git merge --continue (または git rebase --continue)
rerere が有効な場合:
- 一度解決したパターンが記録される
- 同じコンフリクトは自動で解決される
- git rerere status で記録を確認
8. グローバル .gitignore
8.1 設定
# グローバル gitignore を設定
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global
cat << 'EOF' > ~/.gitignore_global
# ─── OS ───
.DS_Store
.DS_Store?
._*
Thumbs.db
Desktop.ini
ehthumbs.db
$RECYCLE.BIN/
# ─── エディタ / IDE ───
*.swp
*.swo
*~
.idea/
*.iml
.vscode/.history
.vscode/settings.json
*.code-workspace
.fleet/
*.sublime-project
*.sublime-workspace
.vs/
# ─── 環境変数 ───
.env
.env.local
.env.*.local
.env.development.local
.env.test.local
.env.production.local
# ─── ビルド / キャッシュ ───
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# ─── ランタイム ───
*.pid
*.seed
*.pid.lock
# ─── その他 ───
.direnv/
.envrc
.tool-versions
.mise.local.toml
.claude/
.cursorrules
*.bak
*.backup
*.orig
EOF8.2 プロジェクト固有の .gitignore
# gitignore.io を使って生成
curl -sL "https://www.toptal.com/developers/gitignore/api/node,react,typescript,vscode,macos" > .gitignore
# gh CLI での生成
gh api /gitignore/templates/Node -q .source >> .gitignore
# よく使う言語・フレームワーク別テンプレート
# https://github.com/github/gitignore9. 複数アカウントの使い分け
9.1 includeIf によるディレクトリベース切替
# ~/.gitconfig
[user]
name = Personal Name
email = personal@example.com
signingkey = PERSONAL_GPG_KEY_ID
# 会社のディレクトリ
[includeIf "gitdir:~/work/"]
path = ~/.gitconfig-work
# OSS プロジェクト用
[includeIf "gitdir:~/oss/"]
path = ~/.gitconfig-oss
# 特定のリポジトリ
[includeIf "gitdir:~/work/secret-project/"]
path = ~/.gitconfig-secret
# リモート URL ベースの切替 (Git 2.36+)
[includeIf "hasconfig:remote.*.url:git@github-work:**"]
path = ~/.gitconfig-work# ~/.gitconfig-work
[user]
name = Work Name
email = work@company.com
signingkey = WORK_GPG_KEY_ID
[core]
sshCommand = ssh -i ~/.ssh/id_ed25519_work
# 会社のプロキシ設定
[http]
proxy = http://proxy.company.com:8080
[url "git@github-work:"]
insteadOf = git@github.com:company/9.2 設定確認スクリプト
#!/bin/bash
# ~/bin/git-check-config.sh
# 現在のディレクトリの Git 設定を確認
echo "=== Git Configuration Check ==="
echo ""
echo "User:"
echo " Name: $(git config user.name)"
echo " Email: $(git config user.email)"
echo ""
echo "Signing:"
echo " Key: $(git config user.signingkey)"
echo " GPG: $(git config commit.gpgsign)"
echo ""
echo "Remote:"
echo " URL: $(git remote get-url origin 2>/dev/null || echo 'N/A')"
echo ""
echo "SSH:"
echo " Command: $(git config core.sshCommand || echo 'default')"
echo ""
echo "Config source:"
git config --show-origin user.email10. Git Hooks
10.1 クライアントサイドフック
# ─── pre-commit フック ───
# .git/hooks/pre-commit (プロジェクト固有)
cat << 'HOOK' > .git/hooks/pre-commit
#!/bin/bash
# Lint & Format チェック
echo "Running pre-commit checks..."
# ステージされたファイルのみ対象
STAGED_FILES=$(git diff --cached --name-only --diff-filter=d)
# TypeScript/JavaScript のリント
TS_FILES=$(echo "$STAGED_FILES" | grep -E '\.(ts|tsx|js|jsx)$')
if [ -n "$TS_FILES" ]; then
echo "Linting TypeScript/JavaScript files..."
npx eslint $TS_FILES --quiet
if [ $? -ne 0 ]; then
echo "❌ ESLint check failed"
exit 1
fi
fi
# 機密情報のチェック
if git diff --cached --diff-filter=d | grep -iE '(password|secret|api_key|token)[\s]*=[\s]*["\x27][^"\x27]+'; then
echo "❌ Possible secret detected in staged changes"
echo "Please review and remove sensitive data"
exit 1
fi
echo "✅ Pre-commit checks passed"
HOOK
chmod +x .git/hooks/pre-commit10.2 Husky + lint-staged (推奨)
# Husky のセットアップ
npx husky init
# lint-staged のインストール
npm install -D lint-staged// package.json
{
"lint-staged": {
"*.{ts,tsx,js,jsx}": [
"eslint --fix",
"prettier --write"
],
"*.{json,md,yml,yaml}": [
"prettier --write"
],
"*.css": [
"stylelint --fix",
"prettier --write"
]
}
}# .husky/pre-commit
npx lint-staged
# .husky/commit-msg
npx --no-install commitlint --edit $1
# .husky/pre-push
npm test10.3 commitlint の設定
# インストール
npm install -D @commitlint/cli @commitlint/config-conventional
# commitlint.config.js
cat << 'EOF' > commitlint.config.js
module.exports = {
extends: ['@commitlint/config-conventional'],
rules: {
'type-enum': [2, 'always', [
'feat', 'fix', 'docs', 'style', 'refactor',
'perf', 'test', 'build', 'ci', 'chore', 'revert'
]],
'subject-max-length': [2, 'always', 72],
'body-max-line-length': [2, 'always', 100],
'header-max-length': [2, 'always', 100],
},
};
EOF11. Git LFS (Large File Storage)
11.1 セットアップ
# インストール
brew install git-lfs
git lfs install
# トラッキング対象の設定
git lfs track "*.psd"
git lfs track "*.zip"
git lfs track "*.mp4"
git lfs track "*.woff2"
git lfs track "*.model" # ML モデル
git lfs track "assets/**" # ディレクトリ単位
# .gitattributes が自動生成される
cat .gitattributes
# *.psd filter=lfs diff=lfs merge=lfs -text
# *.zip filter=lfs diff=lfs merge=lfs -text
# .gitattributes を必ずコミット
git add .gitattributes
git commit -m "chore: configure Git LFS tracking"
# LFS の状態確認
git lfs status
git lfs ls-files # LFS で管理されているファイル一覧
git lfs env # LFS の設定情報12. アンチパターン
12.1 GPG / SSH 鍵にパスフレーズを設定しない
❌ アンチパターン: ssh-keygen 実行時にパスフレーズを空にする
問題:
- 秘密鍵が漏洩した場合、即座に悪用される
- セキュリティ監査で指摘される
- チームのセキュリティポリシー違反
✅ 正しいアプローチ:
- 必ずパスフレーズを設定する
- SSH Agent + Keychain でパスフレーズ入力を自動化
- ssh-add --apple-use-keychain でセッション間で持続
- GPG Agent のキャッシュ時間を適切に設定
12.2 git pull をデフォルトのまま使う
❌ アンチパターン: pull.rebase を設定せずに git pull を実行
問題:
- 不要なマージコミットが大量発生
- git log が読みにくくなる
* abc (HEAD) Merge branch 'main' into feature
|\
| * def (main) Update README
* | ghi Add feature
|/
* jkl Previous commit
✅ 正しいアプローチ:
- git config --global pull.rebase true
- きれいなリニア履歴を維持
* abc (HEAD -> feature) Add feature
* def (main) Update README
* jkl Previous commit
12.3 force push を main/master に実行する
❌ アンチパターン: git push --force origin main
問題:
- 他のメンバーのコミットが消失する
- CI/CD パイプラインが壊れる
- 復旧が困難
✅ 正しいアプローチ:
- feature ブランチでも --force-with-lease を使う
- main/master には force push を禁止する (GitHub Branch Protection)
- GitHub → Settings → Branches → Branch protection rules
✅ Do not allow force pushes
✅ Require pull request reviews before merging
✅ Require status checks to pass before merging
12.4 大きなバイナリファイルを直接コミット
❌ アンチパターン: 画像・動画・モデルファイルを Git に直接コミット
問題:
- リポジトリサイズが膨張
- clone / fetch が遅くなる
- 一度コミットすると履歴から完全削除が困難
✅ 正しいアプローチ:
- Git LFS を使って大きなファイルを管理
- .gitattributes でトラッキングルールを定義
- バイナリファイルの上限サイズを決める (例: 1MB)
- CI で pre-commit フックによるサイズチェック
実践演習
演習1: 基本的な実装
以下の要件を満たすコードを実装してください。
要件:
- 入力データの検証を行うこと
- エラーハンドリングを適切に実装すること
- テストコードも作成すること
# 演習1: 基本実装のテンプレート
class Exercise1:
"""基本的な実装パターンの演習"""
def __init__(self):
self.data = []
def validate_input(self, value):
"""入力値の検証"""
if value is None:
raise ValueError("入力値がNoneです")
return True
def process(self, value):
"""データ処理のメインロジック"""
self.validate_input(value)
self.data.append(value)
return self.data
def get_results(self):
"""処理結果の取得"""
return {
'count': len(self.data),
'data': self.data
}
# テスト
def test_exercise1():
ex = Exercise1()
assert ex.process(1) == [1]
assert ex.process(2) == [1, 2]
assert ex.get_results()['count'] == 2
try:
ex.process(None)
assert False, "例外が発生するべき"
except ValueError:
pass
print("全テスト合格!")
test_exercise1()演習2: 応用パターン
基本実装を拡張して、以下の機能を追加してください。
# 演習2: 応用パターン
from typing import List, Dict, Optional
from datetime import datetime
class AdvancedExercise:
"""応用パターンの演習"""
def __init__(self, max_size: int = 100):
self._items: List[Dict] = []
self._max_size = max_size
self._created_at = datetime.now()
def add(self, key: str, value: any) -> bool:
"""アイテムの追加(サイズ制限付き)"""
if len(self._items) >= self._max_size:
return False
self._items.append({
'key': key,
'value': value,
'timestamp': datetime.now().isoformat()
})
return True
def find(self, key: str) -> Optional[Dict]:
"""キーによる検索"""
for item in reversed(self._items):
if item['key'] == key:
return item
return None
def remove(self, key: str) -> bool:
"""キーによる削除"""
for i, item in enumerate(self._items):
if item['key'] == key:
self._items.pop(i)
return True
return False
def stats(self) -> Dict:
"""統計情報"""
return {
'total_items': len(self._items),
'max_size': self._max_size,
'usage_percent': len(self._items) / self._max_size * 100,
'uptime': str(datetime.now() - self._created_at)
}
# テスト
def test_advanced():
ex = AdvancedExercise(max_size=3)
assert ex.add("a", 1) == True
assert ex.add("b", 2) == True
assert ex.add("c", 3) == True
assert ex.add("d", 4) == False # サイズ制限
assert ex.find("b")['value'] == 2
assert ex.remove("b") == True
assert ex.find("b") is None
stats = ex.stats()
assert stats['total_items'] == 2
print("応用テスト全合格!")
test_advanced()演習3: パフォーマンス最適化
以下のコードのパフォーマンスを改善してください。
# 演習3: パフォーマンス最適化
import time
from functools import lru_cache
# 最適化前(O(n^2))
def slow_search(data: list, target: int) -> int:
"""非効率な検索"""
for i in range(len(data)):
for j in range(i + 1, len(data)):
if data[i] + data[j] == target:
return (i, j)
return (-1, -1)
# 最適化後(O(n))
def fast_search(data: list, target: int) -> tuple:
"""ハッシュマップを使った効率的な検索"""
seen = {}
for i, num in enumerate(data):
complement = target - num
if complement in seen:
return (seen[complement], i)
seen[num] = i
return (-1, -1)
# ベンチマーク
def benchmark():
import random
data = list(range(5000))
random.shuffle(data)
target = data[100] + data[4000]
start = time.time()
result1 = slow_search(data, target)
slow_time = time.time() - start
start = time.time()
result2 = fast_search(data, target)
fast_time = time.time() - start
print(f"非効率版: {slow_time:.4f}秒")
print(f"効率版: {fast_time:.6f}秒")
print(f"高速化率: {slow_time/fast_time:.0f}倍")
benchmark()ポイント:
- アルゴリズムの計算量を意識する
- 適切なデータ構造を選択する
- ベンチマークで効果を測定する
トラブルシューティング
よくあるエラーと解決策
| エラー | 原因 | 解決策 |
|---|---|---|
| 初期化エラー | 設定ファイルの不備 | 設定ファイルのパスと形式を確認 |
| タイムアウト | ネットワーク遅延/リソース不足 | タイムアウト値の調整、リトライ処理の追加 |
| メモリ不足 | データ量の増大 | バッチ処理の導入、ページネーションの実装 |
| 権限エラー | アクセス権限の不足 | 実行ユーザーの権限確認、設定の見直し |
| データ不整合 | 並行処理の競合 | ロック機構の導入、トランザクション管理 |
デバッグの手順
- エラーメッセージの確認: スタックトレースを読み、発生箇所を特定する
- 再現手順の確立: 最小限のコードでエラーを再現する
- 仮説の立案: 考えられる原因をリストアップする
- 段階的な検証: ログ出力やデバッガを使って仮説を検証する
- 修正と回帰テスト: 修正後、関連する箇所のテストも実行する
# デバッグ用ユーティリティ
import logging
import traceback
from functools import wraps
# ロガーの設定
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s [%(levelname)s] %(name)s: %(message)s'
)
logger = logging.getLogger(__name__)
def debug_decorator(func):
"""関数の入出力をログ出力するデコレータ"""
@wraps(func)
def wrapper(*args, **kwargs):
logger.debug(f"呼び出し: {func.__name__}(args={args}, kwargs={kwargs})")
try:
result = func(*args, **kwargs)
logger.debug(f"戻り値: {func.__name__} -> {result}")
return result
except Exception as e:
logger.error(f"例外発生: {func.__name__}: {e}")
logger.error(traceback.format_exc())
raise
return wrapper
@debug_decorator
def process_data(items):
"""データ処理(デバッグ対象)"""
if not items:
raise ValueError("空のデータ")
return [item * 2 for item in items]パフォーマンス問題の診断
パフォーマンス問題が発生した場合の診断手順:
- ボトルネックの特定: プロファイリングツールで計測
- メモリ使用量の確認: メモリリークの有無をチェック
- I/O待ちの確認: ディスクやネットワークI/Oの状況を確認
- 同時接続数の確認: コネクションプールの状態を確認
| 問題の種類 | 診断ツール | 対策 |
|---|---|---|
| CPU負荷 | cProfile, py-spy | アルゴリズム改善、並列化 |
| メモリリーク | tracemalloc, objgraph | 参照の適切な解放 |
| I/Oボトルネック | strace, iostat | 非同期I/O、キャッシュ |
| DB遅延 | EXPLAIN, slow query log | インデックス、クエリ最適化 |
設計判断ガイド
選択基準マトリクス
技術選択を行う際の判断基準を以下にまとめます。
| 判断基準 | 重視する場合 | 妥協できる場合 |
|---|---|---|
| パフォーマンス | リアルタイム処理、大規模データ | 管理画面、バッチ処理 |
| 保守性 | 長期運用、チーム開発 | プロトタイプ、短期プロジェクト |
| スケーラビリティ | 成長が見込まれるサービス | 社内ツール、固定ユーザー |
| セキュリティ | 個人情報、金融データ | 公開データ、社内利用 |
| 開発速度 | MVP、市場投入スピード | 品質重視、ミッションクリティカル |
アーキテクチャパターンの選択
| アーキテクチャ選択フロー |
|---|
| ① チーム規模は? |
| ├─ 小規模(1-5人)→ モノリス |
| └─ 大規模(10人+)→ ②へ |
| ② デプロイ頻度は? |
| ├─ 週1回以下 → モノリス + モジュール分割 |
| └─ 毎日/複数回 → ③へ |
| ③ チーム間の独立性は? |
| ├─ 高い → マイクロサービス |
| └─ 中程度 → モジュラーモノリス |
トレードオフの分析
技術的な判断には必ずトレードオフが伴います。以下の観点で分析を行いましょう:
1. 短期 vs 長期のコスト
- 短期的に速い方法が長期的には技術的負債になることがある
- 逆に、過剰な設計は短期的なコストが高く、プロジェクトの遅延を招く
2. 一貫性 vs 柔軟性
- 統一された技術スタックは学習コストが低い
- 多様な技術の採用は適材適所が可能だが、運用コストが増加
3. 抽象化のレベル
- 高い抽象化は再利用性が高いが、デバッグが困難になる場合がある
- 低い抽象化は直感的だが、コードの重複が発生しやすい
# 設計判断の記録テンプレート
class ArchitectureDecisionRecord:
"""ADR (Architecture Decision Record) の作成"""
def __init__(self, title: str):
self.title = title
self.context = ""
self.decision = ""
self.consequences = []
self.alternatives = []
def set_context(self, context: str):
"""背景と課題の記述"""
self.context = context
return self
def set_decision(self, decision: str):
"""決定内容の記述"""
self.decision = decision
return self
def add_consequence(self, consequence: str, positive: bool = True):
"""結果の追加"""
self.consequences.append({
'description': consequence,
'type': 'positive' if positive else 'negative'
})
return self
def add_alternative(self, name: str, reason_rejected: str):
"""却下した代替案の追加"""
self.alternatives.append({
'name': name,
'reason_rejected': reason_rejected
})
return self
def to_markdown(self) -> str:
"""Markdown形式で出力"""
md = f"# ADR: {self.title}\n\n"
md += f"## 背景\n{self.context}\n\n"
md += f"## 決定\n{self.decision}\n\n"
md += "## 結果\n"
for c in self.consequences:
icon = "✅" if c['type'] == 'positive' else "⚠️"
md += f"- {icon} {c['description']}\n"
md += "\n## 却下した代替案\n"
for a in self.alternatives:
md += f"- **{a['name']}**: {a['reason_rejected']}\n"
return md13. FAQ
Q1: 会社と個人で異なる Git アカウントを使い分けるには?
A: includeIf ディレクティブを使う。~/work/ 配下のリポジトリでは会社の設定を自動適用し、それ以外では個人の設定を使う。SSH の config も分けることで、clone 時に git@github-work:company/repo.git のように使い分けられる。詳細はセクション9を参照。
Q2: rerere とは何?有効にすべき?
A: REuse REcorded REsolution の略。一度解決したコンフリクトのパターンを記録し、同じコンフリクトが再発した際に自動で同じ解決を適用する。feature ブランチを頻繁に rebase する環境では必須レベルの設定。git config --global rerere.enabled true で有効化する。rerere.autoupdate = true にすると、自動解決後にステージングも自動で行われる。
Q3: コミット署名は必要?
A: 個人開発なら任意だが、チーム開発・OSS では強く推奨。GitHub の "Verified" バッジはコミットの真正性を証明する。GitHub Actions の bot コミットがなりすましでないことの確認にも使われる。Organization の設定で署名必須にすることも可能。GPG が面倒なら SSH 鍵署名 (Git 2.34+) が手軽で推奨。
Q4: git push --force と --force-with-lease の違いは?
A: --force はリモートの状態を無条件に上書きする。他のメンバーのコミットを消す危険がある。--force-with-lease はリモートが自分の知っている状態と同じ場合のみ強制 push する。他の人が push していた場合は拒否される。feature ブランチの rebase 後には --force-with-lease を使うべき。main/master への force push は設定で禁止する。
Q5: histogram diff とは何か?
A: diff.algorithm = histogram は patience diff の改良版で、コードの移動や構造的な変更をより賢く検出する。デフォルトの Myers diff よりも「意味のある」差分を生成する傾向がある。特に関数の追加・削除・移動が多いリファクタリング時に効果的。パフォーマンスへの影響はほぼない。
Q6: fsmonitor とは何か?有効にすべき?
A: core.fsmonitor = true はファイルシステムの変更を監視するデーモンを使い、git status や git diff の速度を劇的に改善する。10万ファイル超の大規模リポジトリでは数十倍の高速化が見られる。Git 2.37+ の組み込み FSMonitor か、Facebook の Watchman が使える。小規模リポジトリでは効果が薄いが、有効にしてデメリットはほぼない。
FAQ
Q1: このトピックを学ぶ上で最も重要なポイントは何ですか?
実践的な経験を積むことが最も重要です。理論だけでなく、実際にコードを書いて動作を確認することで理解が深まります。
Q2: 初心者がよく陥る間違いは何ですか?
基礎を飛ばして応用に進むことです。このガイドで説明している基本概念をしっかり理解してから、次のステップに進むことをお勧めします。
Q3: 実務ではどのように活用されていますか?
このトピックの知識は、日常的な開発業務で頻繁に活用されます。特にコードレビューやアーキテクチャ設計の際に重要になります。
14. まとめ
| 設定項目 | 推奨値 | 理由 |
|---|---|---|
init.defaultBranch |
main |
業界標準 |
pull.rebase |
true |
きれいな履歴 |
fetch.prune |
true |
削除済みブランチの自動掃除 |
merge.conflictstyle |
zdiff3 |
共通祖先表示 |
commit.gpgsign |
true |
コミット検証 |
push.autoSetupRemote |
true |
初回 push の手間削減 |
rebase.autosquash |
true |
fixup コミット自動整理 |
rebase.updateRefs |
true |
スタックされた PR の自動更新 |
rerere.enabled |
true |
コンフリクト自動再解決 |
diff.algorithm |
histogram |
より賢い差分検出 |
core.pager |
delta |
美しい diff 表示 |
core.fsmonitor |
true |
大規模リポジトリの高速化 |
branch.sort |
-committerdate |
最新ブランチ優先表示 |
tag.sort |
version:refname |
セマンティックバージョン順 |
help.autocorrect |
prompt |
タイポ時に候補提示 |
次に読むべきガイド
- 03-ai-tools.md -- AI 開発ツールの導入
- ../03-team-setup/00-project-standards.md -- チーム共通の Git 規約
- ../01-runtime-and-package/03-linter-formatter.md -- pre-commit フック連携
参考文献
- Pro Git (2nd Edition) -- https://git-scm.com/book/ja/v2 -- Git の最も包括的な無料リファレンス。日本語版あり。
- GitHub SSH ドキュメント -- https://docs.github.com/ja/authentication/connecting-to-github-with-ssh -- 公式の SSH 設定ガイド。
- git-delta -- https://github.com/dandavison/delta -- delta の公式リポジトリ。設定例が豊富。
- gitconfig のベストプラクティス -- https://jvns.ca/blog/2024/02/16/popular-git-config-options/ -- Julia Evans による実用的な解説。
- Conventional Commits -- https://www.conventionalcommits.org/ja/ -- コミットメッセージ規約の標準仕様。日本語版。
- Git LFS Documentation -- https://git-lfs.com/ -- Git LFS の公式ドキュメント。
- Husky -- https://typicode.github.io/husky/ -- Git hooks の管理ツール。
- GitHub Branch Protection -- https://docs.github.com/en/repositories/configuring-branches-and-merges-in-your-repository/managing-a-branch-protection-rule -- ブランチ保護ルールの設定。
- SSH 鍵署名 (Git Blog) -- https://github.blog/changelog/2022-08-23-ssh-commit-verification-now-supported/ -- SSH 署名の公式アナウンス。
- Git Performance -- https://git-scm.com/docs/git-maintenance -- Git のメンテナンスと最適化コマンド。