Initial commit
This commit is contained in:
381
commands/commit-split.md
Normal file
381
commands/commit-split.md
Normal file
@@ -0,0 +1,381 @@
|
||||
# コミット分割コマンド
|
||||
|
||||
Gitの差分をもとに適切にコミットを分割します。ファイル単位や機能単位で論理的にコミットを分けることで、レビューしやすく意味のあるコミット履歴を作成します。
|
||||
|
||||
## 使用方法
|
||||
```bash
|
||||
/commit-split [ベースブランチ] [分割方式]
|
||||
```
|
||||
|
||||
### パラメータ
|
||||
- `ベースブランチ`: 比較対象のブランチ(省略時は`master`または`main`を自動検出)
|
||||
- `分割方式`: `file`(ファイル単位)、`feature`(機能単位)、`interactive`(インタラクティブ)
|
||||
|
||||
## 処理手順
|
||||
1. 現在の変更状況を確認
|
||||
2. 差分の分析と分割方針の決定
|
||||
3. インタラクティブな分割実行
|
||||
4. 各コミットの作成と確認
|
||||
|
||||
## 実装
|
||||
|
||||
### ステップ1: 初期状態の確認
|
||||
```bash
|
||||
# 作業ディレクトリの状態を確認
|
||||
echo "📊 現在の作業状況を確認中..."
|
||||
git status --porcelain
|
||||
|
||||
# ステージされていない変更があるかチェック
|
||||
UNSTAGED_CHANGES=$(git diff --name-only)
|
||||
STAGED_CHANGES=$(git diff --cached --name-only)
|
||||
|
||||
if [ -n "$UNSTAGED_CHANGES" ] || [ -n "$STAGED_CHANGES" ]; then
|
||||
echo "⚠️ 未コミットの変更があります"
|
||||
echo "📝 ステージされていない変更: $(echo "$UNSTAGED_CHANGES" | wc -l) ファイル"
|
||||
echo "📝 ステージされた変更: $(echo "$STAGED_CHANGES" | wc -l) ファイル"
|
||||
else
|
||||
echo "✅ 作業ディレクトリはクリーンです"
|
||||
exit 0
|
||||
fi
|
||||
```
|
||||
|
||||
### ステップ2: ベースブランチの決定
|
||||
```bash
|
||||
# ベースブランチの自動検出または指定
|
||||
BASE_BRANCH="${1:-}"
|
||||
if [ -z "$BASE_BRANCH" ]; then
|
||||
if git show-ref --verify --quiet refs/heads/master; then
|
||||
BASE_BRANCH="master"
|
||||
elif git show-ref --verify --quiet refs/heads/main; then
|
||||
BASE_BRANCH="main"
|
||||
else
|
||||
echo "❌ ベースブランチを特定できません。明示的に指定してください。"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "🎯 ベースブランチ: $BASE_BRANCH"
|
||||
|
||||
# ベースブランチとの差分を確認
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
echo "🌿 現在のブランチ: $CURRENT_BRANCH"
|
||||
```
|
||||
|
||||
### ステップ3: 差分の分析
|
||||
```bash
|
||||
# 変更されたファイルの一覧と統計
|
||||
echo "📈 差分の分析中..."
|
||||
CHANGED_FILES=$(git diff --name-only $BASE_BRANCH..HEAD 2>/dev/null || git diff --name-only)
|
||||
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
echo "📝 ベースブランチとの差分がありません"
|
||||
# 作業ディレクトリの変更のみ処理
|
||||
CHANGED_FILES=$(git diff --name-only)
|
||||
if [ -z "$CHANGED_FILES" ]; then
|
||||
CHANGED_FILES=$(git diff --cached --name-only)
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "📁 変更されたファイル一覧:"
|
||||
echo "$CHANGED_FILES" | nl
|
||||
|
||||
# ファイルタイプ別の分類
|
||||
DOCS_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(md|txt|rst)$' || true)
|
||||
CONFIG_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(json|yaml|yml|toml|ini|conf)$' || true)
|
||||
CODE_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(js|ts|py|go|rs|java|c|cpp|php|rb)$' || true)
|
||||
TEST_FILES=$(echo "$CHANGED_FILES" | grep -E '(test|spec)\.' || true)
|
||||
|
||||
echo ""
|
||||
echo "📊 ファイルタイプ別統計:"
|
||||
[ -n "$DOCS_FILES" ] && echo "📖 ドキュメント: $(echo "$DOCS_FILES" | wc -l) ファイル"
|
||||
[ -n "$CONFIG_FILES" ] && echo "⚙️ 設定ファイル: $(echo "$CONFIG_FILES" | wc -l) ファイル"
|
||||
[ -n "$CODE_FILES" ] && echo "💻 コードファイル: $(echo "$CODE_FILES" | wc -l) ファイル"
|
||||
[ -n "$TEST_FILES" ] && echo "🧪 テストファイル: $(echo "$TEST_FILES" | wc -l) ファイル"
|
||||
```
|
||||
|
||||
### ステップ4: 分割方式の決定
|
||||
```bash
|
||||
SPLIT_MODE="${2:-file}"
|
||||
|
||||
case "$SPLIT_MODE" in
|
||||
"file")
|
||||
echo "📂 ファイル単位での分割を実行します"
|
||||
commit_by_file
|
||||
;;
|
||||
"feature")
|
||||
echo "🎯 機能単位での分割を実行します"
|
||||
commit_by_feature
|
||||
;;
|
||||
"interactive"|*)
|
||||
echo "🤝 インタラクティブ分割を実行します"
|
||||
commit_interactive
|
||||
;;
|
||||
esac
|
||||
```
|
||||
|
||||
### ステップ5: ファイル単位分割の実装
|
||||
```bash
|
||||
commit_by_file() {
|
||||
echo "$CHANGED_FILES" | while read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
echo ""
|
||||
echo "📝 処理中: $file"
|
||||
|
||||
# ファイルの変更内容を表示
|
||||
git diff --stat "$file" 2>/dev/null || git diff --cached --stat "$file" 2>/dev/null || true
|
||||
|
||||
# コミットメッセージの自動生成
|
||||
FILE_EXT="${file##*.}"
|
||||
DIR_NAME=$(dirname "$file")
|
||||
FILE_NAME=$(basename "$file")
|
||||
|
||||
# ファイルタイプに基づいたプレフィックス
|
||||
if echo "$file" | grep -q -E '\.(md|txt|rst)$'; then
|
||||
PREFIX="docs"
|
||||
elif echo "$file" | grep -q -E '\.(json|yaml|yml|toml|ini|conf)$'; then
|
||||
PREFIX="config"
|
||||
elif echo "$file" | grep -q -E '(test|spec)\.'; then
|
||||
PREFIX="test"
|
||||
else
|
||||
PREFIX="feat"
|
||||
fi
|
||||
|
||||
COMMIT_MSG="$PREFIX: update $FILE_NAME"
|
||||
|
||||
# 自動でコミット(確認なし)
|
||||
echo "💬 提案されたコミットメッセージ: $COMMIT_MSG"
|
||||
git add "$file"
|
||||
git commit -m "$COMMIT_MSG"
|
||||
echo "✅ コミット完了: $file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
### ステップ6: 機能単位分割の実装
|
||||
```bash
|
||||
commit_by_feature() {
|
||||
# ディレクトリ構造に基づいた機能別グループ化
|
||||
echo "🔍 機能別にファイルをグループ化中..."
|
||||
|
||||
# ディレクトリごとにファイルをグループ化
|
||||
DIRECTORIES=$(echo "$CHANGED_FILES" | xargs dirname | sort | uniq)
|
||||
|
||||
echo "$DIRECTORIES" | while read -r dir; do
|
||||
if [ -n "$dir" ]; then
|
||||
DIR_FILES=$(echo "$CHANGED_FILES" | grep "^$dir/")
|
||||
|
||||
if [ -n "$DIR_FILES" ]; then
|
||||
echo ""
|
||||
echo "📁 ディレクトリ: $dir"
|
||||
echo "$DIR_FILES" | sed 's/^/ - /'
|
||||
|
||||
# ディレクトリ名に基づいたコミットメッセージ
|
||||
case "$dir" in
|
||||
"src"|"lib"|"app")
|
||||
PREFIX="feat"
|
||||
;;
|
||||
"test"|"tests"|"__tests__")
|
||||
PREFIX="test"
|
||||
;;
|
||||
"docs"|"doc")
|
||||
PREFIX="docs"
|
||||
;;
|
||||
"config"|".github")
|
||||
PREFIX="config"
|
||||
;;
|
||||
*)
|
||||
PREFIX="update"
|
||||
;;
|
||||
esac
|
||||
|
||||
COMMIT_MSG="$PREFIX: update $dir module"
|
||||
|
||||
echo "💬 提案されたコミットメッセージ: $COMMIT_MSG"
|
||||
echo "$DIR_FILES" | xargs git add
|
||||
git commit -m "$COMMIT_MSG"
|
||||
echo "✅ コミット完了: $dir"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
}
|
||||
```
|
||||
|
||||
### ステップ7: インタラクティブ分割の実装
|
||||
```bash
|
||||
commit_interactive() {
|
||||
echo "🎮 自動モードでコミットを分割します"
|
||||
|
||||
# 現在の状態を確認
|
||||
REMAINING_UNSTAGED=$(git diff --name-only)
|
||||
REMAINING_STAGED=$(git diff --cached --name-only)
|
||||
|
||||
if [ -z "$REMAINING_UNSTAGED" ] && [ -z "$REMAINING_STAGED" ]; then
|
||||
echo "📝 変更がありません"
|
||||
return
|
||||
fi
|
||||
|
||||
# 全ファイルをステージして一度にコミット
|
||||
git add .
|
||||
commit_staged_changes
|
||||
|
||||
echo "🎉 すべての変更が処理されました!"
|
||||
}
|
||||
|
||||
select_files_for_staging() {
|
||||
ALL_CHANGED=$(git status --porcelain | awk '{print $2}')
|
||||
if [ -z "$ALL_CHANGED" ]; then
|
||||
echo "📝 変更されたファイルがありません"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "📁 変更されたファイル一覧:"
|
||||
echo "$ALL_CHANGED" | nl
|
||||
echo ""
|
||||
|
||||
# 全ファイルを自動でステージ
|
||||
echo "$ALL_CHANGED" | while read -r file; do
|
||||
if [ -n "$file" ]; then
|
||||
git add "$file"
|
||||
echo "✅ ステージ完了: $file"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
commit_staged_changes() {
|
||||
STAGED=$(git diff --cached --name-only)
|
||||
if [ -z "$STAGED" ]; then
|
||||
echo "📦 ステージされた変更がありません"
|
||||
return
|
||||
fi
|
||||
|
||||
echo "📦 ステージされたファイル:"
|
||||
echo "$STAGED" | sed 's/^/ - /'
|
||||
echo ""
|
||||
|
||||
# 自動でコミット
|
||||
commit_msg="update: staged changes"
|
||||
git commit -m "$commit_msg"
|
||||
echo "✅ コミット完了!"
|
||||
}
|
||||
|
||||
show_diff_summary() {
|
||||
echo "📊 変更サマリー:"
|
||||
git diff --stat
|
||||
echo ""
|
||||
git diff --cached --stat
|
||||
echo ""
|
||||
# 詳細な差分を自動的に表示
|
||||
echo "📖 詳細な差分:"
|
||||
git diff
|
||||
git diff --cached
|
||||
}
|
||||
|
||||
auto_push_changes() {
|
||||
# 現在のブランチ名を取得
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
|
||||
echo "📤 リモートブランチにプッシュ中..."
|
||||
|
||||
# リモートブランチが存在するかチェック
|
||||
if git ls-remote --heads origin "$CURRENT_BRANCH" | grep -q "$CURRENT_BRANCH"; then
|
||||
# リモートブランチが存在する場合は通常のpush
|
||||
git push
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ プッシュ完了: $CURRENT_BRANCH"
|
||||
else
|
||||
echo "⚠️ プッシュでエラーが発生しました"
|
||||
fi
|
||||
else
|
||||
# リモートブランチが存在しない場合は -u オプション付きでpush
|
||||
git push -u origin "$CURRENT_BRANCH"
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "✅ 新しいブランチを作成してプッシュ完了: $CURRENT_BRANCH"
|
||||
else
|
||||
echo "⚠️ プッシュでエラーが発生しました"
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
show_help() {
|
||||
echo "📚 コミット分割ヘルプ"
|
||||
echo ""
|
||||
echo "🎯 目的: 大きな変更を論理的に分割して複数のコミットに分ける"
|
||||
echo ""
|
||||
echo "💡 推奨される分割方針:"
|
||||
echo " - 📁 ファイルタイプ別(設定、ドキュメント、コード、テスト)"
|
||||
echo " - 🎯 機能別(新機能、バグ修正、リファクタリング)"
|
||||
echo " - 📦 影響範囲別(フロントエンド、バックエンド、DB)"
|
||||
echo ""
|
||||
echo "🔧 便利なGitコマンド:"
|
||||
echo " - git add -p : パッチ単位での選択的ステージング"
|
||||
echo " - git diff --cached : ステージされた変更の確認"
|
||||
echo " - git reset <file> : ファイルのアンステージ"
|
||||
echo " - git status : 現在の状態確認"
|
||||
echo ""
|
||||
echo "📤 プッシュ機能:"
|
||||
echo " - 各コミット後に個別でプッシュするか選択可能"
|
||||
echo " - 最終処理で全てのコミットを一括プッシュ"
|
||||
}
|
||||
```
|
||||
|
||||
### ステップ8: 完了処理とプッシュ
|
||||
```bash
|
||||
# 最終確認と統計
|
||||
echo ""
|
||||
echo "🎉 コミット分割が完了しました!"
|
||||
echo ""
|
||||
echo "📈 作成されたコミット:"
|
||||
git log --oneline -10
|
||||
|
||||
echo ""
|
||||
echo "📤 自動プッシュを実行します..."
|
||||
|
||||
# 現在のブランチ名を取得
|
||||
CURRENT_BRANCH=$(git branch --show-current)
|
||||
|
||||
# リモートブランチが存在するかチェック
|
||||
if git ls-remote --heads origin "$CURRENT_BRANCH" | grep -q "$CURRENT_BRANCH"; then
|
||||
# リモートブランチが存在する場合は通常のpush
|
||||
git push
|
||||
echo "✅ プッシュ完了: $CURRENT_BRANCH"
|
||||
else
|
||||
# リモートブランチが存在しない場合は -u オプション付きでpush
|
||||
git push -u origin "$CURRENT_BRANCH"
|
||||
echo "✅ 新しいブランチを作成してプッシュ完了: $CURRENT_BRANCH"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "🚀 すべてのコミットがリモートに反映されました!"
|
||||
```
|
||||
|
||||
## 重要なルール
|
||||
- **段階的なコミット**: 関連する変更をまとめて、レビューしやすい単位でコミット
|
||||
- **意味のあるメッセージ**: 各コミットが何を変更したかを明確に記述
|
||||
- **ファイルタイプの考慮**: 設定ファイル、ドキュメント、コード、テストを適切に分離
|
||||
- **レビュー性の向上**: 1つのコミットで1つの論理的変更を実現
|
||||
- **自動プッシュ**: 各コミット後にプッシュ選択可能、最終的に全コミットをリモートに反映
|
||||
|
||||
## 使用例
|
||||
|
||||
### ファイル単位での分割
|
||||
```bash
|
||||
/commit-split master file
|
||||
```
|
||||
|
||||
### 機能単位での分割
|
||||
```bash
|
||||
/commit-split main feature
|
||||
```
|
||||
|
||||
### インタラクティブ分割
|
||||
```bash
|
||||
/commit-split
|
||||
```
|
||||
|
||||
## 注意事項
|
||||
- 実行前に重要な変更はバックアップを取ってください
|
||||
- `git add -p` を使用する際は、パッチの内容をよく確認してください
|
||||
- 各コミット後にプッシュするかどうかを選択できます
|
||||
- 最終的にすべてのコミットがリモートに反映されます
|
||||
- リモートにプッシュ済みのコミットは分割しないでください
|
||||
Reference in New Issue
Block a user