Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:45:11 +08:00
commit 42c0b6ee81
16 changed files with 3608 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
---
name: get-git-diff
description: Examines git diffs between commits or branches with intelligent analysis. Provides unified diff format with comprehensive summaries including file statistics, rename detection, and merge commit handling. Outputs formatted diffs to /claudedocs for documentation and review purposes.
---
# Git Diff Analyzer
## <20> MANDATORY COMPLIANCE <20>
**CRITICAL**: The 4-step workflow outlined in this document MUST be followed in exact order for EVERY diff analysis. Skipping steps or deviating from the procedure will result in incomplete analysis. This is non-negotiable.
## File Structure
- **SKILL.md** (this file): Main instructions and MANDATORY workflow
- **examples.md**: Usage scenarios with different diff types
- **../../context/git/**: Shared git context files
- `git_diff_reference.md`: Unified diff format reference and best practices
- `diff_patterns.md`: Common patterns to identify in code changes
- **../../memory/skills/get-git-diff/**: Project-specific diff analysis memory
- `{project-name}/`: Per-project diff patterns and insights
- **scripts/**:
- `README.md`: Complete documentation for all helper scripts
- `validate.sh`: Git repository and commit validation functions
- `commit_info.sh`: Commit metadata retrieval (hash, author, date, message)
- `diff_stats.sh`: Diff statistics and line count analysis
- `file_operations.sh`: File operation detection (add, modify, delete, rename)
- `utils.sh`: General utilities (branch detection, formatting, repo info)
- **templates/**:
- `output_template.md`: Standard output format template
## Analysis Focus Areas
Git diff analysis evaluates 7 critical dimensions:
1. **Change Scope**: Files affected, lines modified, overall impact radius
2. **Change Type**: Feature addition, bug fix, refactoring, configuration change
3. **Structural Changes**: File renames, moves, deletions, additions
4. **Risk Assessment**: Breaking changes, API modifications, database migrations
5. **Code Quality Impact**: Complexity changes, test coverage changes
6. **Merge Conflicts**: Merge commit analysis, conflict resolution patterns
7. **Performance Impact**: Algorithm changes, database query modifications, resource usage
**Note**: Analysis depth is summary-level, focusing on what changed and high-level impact.
---
## MANDATORY WORKFLOW (MUST FOLLOW EXACTLY)
### <20> STEP 1: Commit Identification (REQUIRED)
**YOU MUST:**
1. Check if commit hashes/branch names were provided in the triggering prompt
2. If NOT provided, ask the user with these options:
- **Option A**: Compare specific commit hashes (ask for two commit SHAs)
- **Option B**: Compare HEAD of current branch to main/master
- **Option C**: Compare two branch names
- **Option D**: Compare current changes to a specific commit
3. Validate that provided commits/branches exist in the repository
4. Use `git rev-parse` to verify and get full commit hashes
5. Get commit metadata (author, date, message) for both commits
**DO NOT PROCEED WITHOUT VALID COMMITS**
### <20> STEP 2: Execute Git Diff with Special Handling (REQUIRED)
**YOU MUST:**
1. Execute `git diff [commit1]...[commit2]` to get the unified diff
2. Check for and handle special cases:
- **Large diffs** (>1000 lines): Warn user, offer to summarize only or proceed
- **Renamed files**: Use `git diff -M` to detect renames
- **Merge commits**: Use `git diff [commit]^...[commit]` for merge commit analysis
- **Binary files**: Note binary file changes separately
3. Get diff statistics with `git diff --stat`
4. Get file list with `git diff --name-status` to identify A/M/D/R operations
**DO NOT SKIP SPECIAL CASE DETECTION**
### <20> STEP 3: Analyze and Summarize (REQUIRED)
**YOU MUST analyze and document**:
1. **Commit Metadata**:
- Commit hashes (full and short)
- Author and date for both commits
- Commit messages
- Number of commits between the two refs (if applicable)
2. **Change Statistics**:
- Total files changed
- Total insertions (+)
- Total deletions (-)
- Net change
3. **File Operations**:
- Added files (A)
- Modified files (M)
- Deleted files (D)
- Renamed files (R) - show old vs new
- Copied files (C)
4. **Change Categorization**:
- Group files by type (source code, tests, docs, config)
- Identify potential areas of impact
- Flag potentially risky changes
5. **Special Notes**:
- Merge commit indicator (if applicable)
- Large diff warning (if >1000 lines)
- Binary file changes
- Submodule changes
**DO NOT SKIP ANALYSIS**
### <20> STEP 4: Generate Output & Update Project Memory (REQUIRED)
**YOU MUST:**
1. Use the template from `templates/output_template.md`
2. Create filename: `diff_{short_hash1}_{short_hash2}.md`
3. Include all components:
- Header with commit information
- Summary section with all statistics and analysis
- Full unified diff wrapped in markdown code blocks
4. Save to `/claudedocs/` directory
5. Confirm file was written successfully
**Output Format Requirements**:
- Unified diff must be in triple-backtick code blocks with `diff` language tag
- Summary must be in clear markdown sections
- File paths must use code formatting
- Statistics must be in tables or lists
- All sections must be clearly labeled
**DO NOT OMIT ANY REQUIRED SECTIONS**
**OPTIONAL: Update Project Memory**
If patterns emerge during analysis, consider storing insights in `../../memory/skills/get-git-diff/{project-name}/`:
- Common file change patterns
- Frequently modified areas
- Notable commit patterns or conventions
---
## Special Case Handling
### Large Diffs (>1000 lines)
When encountering large diffs:
1. Calculate total line count
2. Warn user: "This diff contains [N] lines across [M] files"
3. Ask user: "Would you like to proceed with full diff or summary only?"
4. If summary only:
- Include all metadata and statistics
- List all changed files with their line counts
- Omit the detailed unified diff
- Note: "Full diff omitted due to size. Use `git diff [hash1]...[hash2]` to view."
### Renamed/Moved Files
For file renames:
1. Use `git diff -M` flag to detect renames (default similarity index: 50%)
2. In summary, clearly show: `old/path/file.py <20> new/path/file.py`
3. Indicate if content was also modified: `R+M` (renamed and modified)
4. In unified diff, show rename header: `rename from/to`
### Merge Commits
For merge commits:
1. Detect with `git rev-list --merges`
2. Note in summary: "This is a merge commit"
3. Show both parent commits
4. Use `git diff [commit]^...[commit]` to show changes introduced by merge
5. Optionally offer to show diff against each parent separately
---
## Compliance Checklist
Before completing ANY diff analysis, verify:
- [ ] Step 1: Commits identified and validated
- [ ] Step 2: Git diff executed with special case detection
- [ ] Step 3: Complete analysis with all statistics and categorization
- [ ] Step 4: Output generated in correct format and saved to /claudedocs
**FAILURE TO COMPLETE ALL STEPS INVALIDATES THE ANALYSIS**
---
## Output File Naming Convention
**Format**: `diff_{short1}_{short2}.md`
Where:
- `{short1}` = First 7 characters of first commit hash
- `{short2}` = First 7 characters of second commit hash
**Examples**:
- `diff_a1b2c3d_e4f5g6h.md` (commit to commit)
- `diff_main_feature-branch.md` (branch comparison, if hashes not available)
**Alternative for branches**: If comparing branch tips, you may use branch names if they're short and filesystem-safe.
---
## Git Commands Reference
### Core Commands Used:
```bash
# Get commit info
git rev-parse [commit]
git log -1 --format="%H|%h|%an|%ae|%ad|%s" [commit]
# Generate diff
git diff [commit1]...[commit2]
git diff --stat [commit1]...[commit2]
git diff --name-status [commit1]...[commit2]
git diff -M [commit1]...[commit2] # Detect renames
# Special cases
git rev-list --merges [commit] # Check if merge commit
git diff [commit]^1..[commit] # Merge commit against first parent
```
---
## Further Reading
Refer to official documentation:
- **Git Documentation**:
- Git Diff: https://git-scm.com/docs/git-diff
- Diff Format: https://git-scm.com/docs/diff-format
- **Best Practices**:
- Pro Git Book: https://git-scm.com/book/en/v2
- Understanding Git Diff: https://git-scm.com/docs/git-diff#_generating_patches_with_p
---
## Version History
- v1.1.0 (2025-01-XX): Centralized context and project memory
- Context files moved to ../../context/git/
- Project-specific memory system in ../../memory/skills/get-git-diff/
- Optional memory updates for common patterns
- v1.0.0 (2025-11-13): Initial release
- Mandatory 4-step workflow
- Summary-level analysis with statistics
- Special handling for large diffs, renames, and merge commits
- Unified diff output to /claudedocs

View File

@@ -0,0 +1,510 @@
# Git Diff Analyzer Examples
This file contains example scenarios demonstrating how to use the get-git-diff skill for various diff analysis tasks.
---
## Example 1: Comparing Two Specific Commits
### Scenario
Developer wants to see what changed between two feature commits.
### User Prompt
```
I need to see the diff between commits a1b2c3d and e4f5g6h
```
### Skill Execution
**Step 1 - Commit Identification**:
- Commits provided: `a1b2c3d` and `e4f5g6h`
- Validate: `git rev-parse a1b2c3d e4f5g6h`
- Get metadata: `git log -1 --format="%H|%h|%an|%ae|%ad|%s" [commit]`
**Step 2 - Execute Diff**:
```bash
git diff a1b2c3d...e4f5g6h
git diff --stat a1b2c3d...e4f5g6h
git diff --name-status a1b2c3d...e4f5g6h
```
**Step 3 - Analysis**:
```
Commit Metadata:
- From: a1b2c3d (John Doe, 2025-11-01, "Add user authentication")
- To: e4f5g6h (Jane Smith, 2025-11-13, "Refactor auth module")
- Commits between: 15
Statistics:
- Files changed: 8
- Insertions: 234
- Deletions: 156
- Net change: +78
File Operations:
- Modified: src/auth/login.py, src/auth/session.py, tests/test_auth.py
- Added: src/auth/jwt_handler.py, src/auth/validators.py
- Deleted: src/auth/legacy_auth.py
```
**Step 4 - Output**:
File: `/claudedocs/diff_a1b2c3d_e4f5g6h.md`
---
## Example 2: Comparing Current Branch to Main
### Scenario
Developer wants to review all changes in feature branch before creating a PR.
### User Prompt
```
Show me the diff for my feature branch compared to main
```
### Skill Response
Since no specific commits provided, ask user:
- Option A: Compare specific commit hashes
- Option B: Compare HEAD of current branch to main ← **User selects this**
- Option C: Compare two branch names
- Option D: Compare current changes to a specific commit
**Step 1 - Commit Identification**:
```bash
# Get current branch
git rev-parse --abbrev-ref HEAD # Returns: feature/new-api
# Get commit hashes
git rev-parse main # Returns: d7e8f9a...
git rev-parse HEAD # Returns: m3n4o5p...
```
**Step 2 - Execute Diff**:
```bash
git diff main...HEAD
git diff --stat main...HEAD
git diff --name-status main...HEAD
```
**Step 3 - Analysis**:
```
Comparing: main (d7e8f9a) → feature/new-api (m3n4o5p)
Statistics:
- Files changed: 12
- Insertions: 567
- Deletions: 89
- Net change: +478
File Operations:
- Added: src/api/v2/endpoints.py, src/api/v2/schemas.py, tests/api/test_v2.py
- Modified: src/api/router.py, src/main.py, README.md
- Deleted: (none)
Change Categorization:
- Source code: 8 files (src/api/*, src/main.py)
- Tests: 3 files (tests/api/*)
- Documentation: 1 file (README.md)
Impact Areas:
- API routing layer
- Request/response schemas
- Test coverage
```
**Step 4 - Output**:
File: `/claudedocs/diff_d7e8f9a_m3n4o5p.md`
---
## Example 3: Large Diff Handling
### Scenario
Comparing two commits with extensive changes (e.g., major refactoring or dependency update).
### User Prompt
```
Compare commit abc1234 to def5678
```
**Step 2 - Execute Diff**:
```bash
git diff --stat abc1234...def5678
# Output shows: 87 files changed, 15234 insertions(+), 12456 deletions(-)
```
**Large Diff Detected**: Total lines = 27,690
**Skill Response**:
⚠️ **Large Diff Warning**
This diff contains 27,690 lines across 87 files. This is a substantial change set.
Would you like to:
- **Option A**: Proceed with full diff (will be very long)
- **Option B**: Summary only (statistics and file list, no line-by-line diff)
**User selects**: Option B - Summary only
**Step 3 - Analysis**:
```
Commit Metadata:
- From: abc1234 (Team Lead, 2025-10-15, "Update dependencies to Python 3.12")
- To: def5678 (Team Lead, 2025-11-13, "Complete Python 3.12 migration")
Statistics:
- Files changed: 87
- Insertions: 15,234
- Deletions: 12,456
- Net change: +2,778
Top Changed Files:
1. requirements.txt (+45, -32)
2. src/core/utils.py (+234, -189)
3. tests/test_compatibility.py (+456, -123)
... (84 more files)
Change Categorization:
- Dependency files: 3 files
- Source code: 62 files
- Tests: 18 files
- Documentation: 4 files
Impact Assessment:
⚠️ Major refactoring detected
⚠️ Breaking changes likely
⚠️ Extensive test updates
```
**Step 4 - Output**:
File: `/claudedocs/diff_abc1234_def5678.md`
**Note in output**: "Full unified diff omitted due to size (27,690 lines). Use `git diff abc1234...def5678` to view complete diff."
---
## Example 4: Renamed Files Detection
### Scenario
Developer refactored project structure, moving files to new directories.
### User Prompt
```
Show me what changed in the refactoring commit x9y8z7w
```
**Step 1 - Commit Identification**:
- Comparing: x9y8z7w^ (parent) → x9y8z7w (the refactoring commit)
**Step 2 - Execute Diff with Rename Detection**:
```bash
git diff -M x9y8z7w^...x9y8z7w
git diff --name-status -M x9y8z7w^...x9y8z7w
```
Output:
```
R100 src/utils.py → src/core/utils.py
R100 src/helpers.py → src/core/helpers.py
R085 src/auth.py → src/auth/main.py
M src/main.py
A src/core/__init__.py
```
**Step 3 - Analysis**:
```
Commit Metadata:
- Commit: x9y8z7w (Developer, 2025-11-12, "Refactor: reorganize project structure")
Statistics:
- Files changed: 6
- Insertions: 45
- Deletions: 12
- Net change: +33
File Operations:
- Renamed (no changes):
• src/utils.py → src/core/utils.py
• src/helpers.py → src/core/helpers.py
- Renamed + Modified (85% similarity):
• src/auth.py → src/auth/main.py
- Modified:
• src/main.py (updated imports)
- Added:
• src/core/__init__.py (new package init)
Change Type: Refactoring
Risk Assessment: Medium (import paths changed, may break external references)
```
**Step 4 - Output**:
File: `/claudedocs/diff_w6v5u4t_x9y8z7w.md`
---
## Example 5: Merge Commit Analysis
### Scenario
Developer wants to understand what a merge commit introduced.
### User Prompt
```
Analyze the merge commit m1e2r3g4
```
**Step 1 - Commit Identification**:
```bash
git rev-list --merges m1e2r3g4^..m1e2r3g4
# Confirms: m1e2r3g4 is a merge commit
git log -1 --format="%P" m1e2r3g4
# Returns: a1b2c3d e4f5g6h (two parents)
```
**Skill Response**:
⚠️ **Merge Commit Detected**
This is a merge commit with two parents:
- Parent 1: a1b2c3d (main branch)
- Parent 2: e4f5g6h (feature branch)
**Step 2 - Execute Diff**:
```bash
# Show changes introduced by the merge (against first parent)
git diff m1e2r3g4^1...m1e2r3g4
git diff --stat m1e2r3g4^1...m1e2r3g4
```
**Step 3 - Analysis**:
```
Merge Commit Metadata:
- Commit: m1e2r3g4
- Author: John Doe
- Date: 2025-11-13
- Message: "Merge feature/user-profiles into main"
- Parent 1 (base): a1b2c3d (main)
- Parent 2 (merged): e4f5g6h (feature/user-profiles)
Statistics:
- Files changed: 15
- Insertions: 892
- Deletions: 134
- Net change: +758
Merged Branch Summary:
- Branch: feature/user-profiles
- Commits merged: 23
- Primary changes:
• New user profile management system
• Profile API endpoints
• Profile database models
• Associated tests
Change Categorization:
- Source code: 9 files (src/profiles/*, src/api/profiles.py)
- Database: 2 files (migrations/*, models/profile.py)
- Tests: 3 files (tests/profiles/*)
- Documentation: 1 file (docs/api.md)
Merge Notes:
✓ No merge conflicts (clean merge)
✓ All tests passing (based on commit message)
```
**Step 4 - Output**:
File: `/claudedocs/diff_a1b2c3d_m1e2r3g4.md`
Special note in file: "This is a merge commit analysis showing changes introduced by merging feature/user-profiles into main."
---
## Example 6: Comparing Branches by Name
### Scenario
Developer wants to see differences between two branches without knowing commit hashes.
### User Prompt
```
Compare the staging branch to the production branch
```
**Skill Response**:
No specific commits provided. Asking user for comparison target...
User selects: **Option C - Compare two branch names**
Please provide:
- First branch: **production**
- Second branch: **staging**
**Step 1 - Commit Identification**:
```bash
git rev-parse production # Returns: p1r2o3d...
git rev-parse staging # Returns: s4t5a6g...
git log --oneline production..staging
# Shows: 8 commits ahead
```
**Step 3 - Analysis**:
```
Comparing Branches:
- production (p1r2o3d): "Release v2.1.0" (2025-11-01)
- staging (s4t5a6g): "Add feature X" (2025-11-13)
- Commits ahead: 8
- Commits behind: 0
Statistics:
- Files changed: 24
- Insertions: 1,234
- Deletions: 456
- Net change: +778
File Operations:
- Added: 8 files (new features)
- Modified: 14 files (enhancements)
- Deleted: 2 files (deprecated code)
Impact Assessment:
✓ Ready for production deployment
✓ All features tested in staging
⚠️ Database migration required (see migrations/0023_add_feature_x.py)
```
**Step 4 - Output**:
File: `/claudedocs/diff_p1r2o3d_s4t5a6g.md`
---
## Example 7: Current Working Directory Changes
### Scenario
Developer has uncommitted changes and wants to see diff against last commit.
### User Prompt
```
Show me my current uncommitted changes
```
**Skill Response**:
No specific commits provided. Asking user...
User selects: **Option D - Compare current changes to a specific commit**
Please provide the commit to compare against (or press Enter for HEAD): **[User presses Enter]**
**Step 1 - Commit Identification**:
```bash
# Comparing working directory against HEAD
git rev-parse HEAD # Returns: h3e4a5d...
```
**Step 2 - Execute Diff**:
```bash
git diff HEAD
git diff --stat HEAD
git diff --name-status HEAD
```
**Step 3 - Analysis**:
```
Working Directory Changes:
- Base commit: h3e4a5d (HEAD, "Latest commit")
- Status: Uncommitted changes
Statistics:
- Files changed: 3
- Insertions: 67
- Deletions: 23
- Net change: +44
File Operations:
- Modified:
• src/api/endpoints.py (+45, -12)
• tests/test_api.py (+18, -8)
• README.md (+4, -3)
Change Type: Feature development (in progress)
Status: ⚠️ Uncommitted - not yet in version control
```
**Step 4 - Output**:
File: `/claudedocs/diff_h3e4a5d_working.md`
---
## Example 8: Binary Files in Diff
### Scenario
Diff includes binary file changes (images, PDFs, compiled files).
### User Prompt
```
Compare commit old123 to new456
```
**Step 2 - Execute Diff**:
```bash
git diff --stat old123...new456
git diff --name-status old123...new456
```
Output includes:
```
M docs/architecture.pdf
M static/images/logo.png
A static/images/hero.jpg
```
**Step 3 - Analysis**:
```
Statistics:
- Files changed: 15
- Text files: 12 (analyzed in diff)
- Binary files: 3 (listed separately)
Text File Changes:
- Insertions: 234
- Deletions: 156
- Net change: +78
Binary File Changes:
- Modified:
• docs/architecture.pdf (binary file changed)
• static/images/logo.png (binary file changed)
- Added:
• static/images/hero.jpg (new binary file)
Note: Binary file content changes not shown in diff.
Use `git show old123:path` and `git show new456:path` to extract binary files.
```
**Step 4 - Output**:
File includes note: "⚠️ This diff contains 3 binary files. Binary content not displayed. See summary for list of affected binary files."
---
## Summary of Use Cases
1. **Two specific commits** - Direct comparison with full metadata
2. **Branch to main** - Pre-PR review workflow
3. **Large diffs** - Summary-only option for massive changes
4. **Renamed files** - Detect and document file restructuring
5. **Merge commits** - Special handling with parent information
6. **Branch comparison** - Compare branch tips by name
7. **Working directory** - Review uncommitted changes
8. **Binary files** - Special notation for non-text files
## Best Practices
- Always validate commits exist before running diff
- For large diffs, offer summary option first
- Clearly indicate merge commits with special notation
- Show both old and new paths for renamed files
- Categorize changes by file type and impact area
- Provide actionable insights in the summary
- Save output with descriptive filenames
- Include enough metadata for audit trail

View File

@@ -0,0 +1,240 @@
# Git Diff Helper Scripts
This directory contains bash utility scripts for git diff operations used by the get-git-diff skill.
## Scripts Overview
### validate.sh
Validation and verification functions.
**Functions:**
- `is_git_repo()` - Check if in a git repository
- `validate_commit <ref>` - Validate a commit reference
- `validate_commit_pair <ref1> <ref2>` - Validate two commits
**Usage:**
```bash
source validate.sh
if is_git_repo; then
validate_commit "HEAD"
fi
```
### commit_info.sh
Commit information and metadata retrieval.
**Functions:**
- `get_commit_info <ref>` - Get full commit information
- `get_short_hash <ref>` - Get 7-character hash
- `is_merge_commit <ref>` - Check if merge commit
- `get_commits_between <ref1> <ref2>` - Count commits between refs
- `get_commit_message <ref>` - Get commit message
- `get_commit_author <ref>` - Get commit author
- `get_commit_date <ref>` - Get commit date
**Usage:**
```bash
source commit_info.sh
info=$(get_commit_info "HEAD")
IFS='|' read -r full short author email date message <<< "$info"
echo "Commit: $short - $message"
```
### diff_stats.sh
Diff statistics and analysis.
**Functions:**
- `get_diff_stats <ref1> <ref2>` - Get diff statistics
- `get_file_stats <ref1> <ref2>` - Get per-file statistics
- `is_large_diff <ref1> <ref2> [threshold]` - Check if diff is large
- `get_total_lines <ref1> <ref2>` - Get total lines changed
- `get_files_changed <ref1> <ref2>` - Get file count
**Usage:**
```bash
source diff_stats.sh
stats=$(get_diff_stats "HEAD^" "HEAD")
IFS=$'\t' read -r files ins del net <<< "$stats"
echo "Changed: $files files, +$ins -$del"
```
### file_operations.sh
File operation analysis (add, modify, delete, rename).
**Functions:**
- `get_file_operations <ref1> <ref2>` - Get file operations
- `count_file_operations <ref1> <ref2>` - Count operations by type
- `get_files_by_operation <ref1> <ref2> <type>` - Filter by operation (A/M/D/R)
- `get_renamed_files <ref1> <ref2>` - Get renames with similarity
- `get_binary_files <ref1> <ref2>` - Get binary files
- `count_binary_files <ref1> <ref2>` - Count binary files
- `categorize_files` - Categorize files by type (reads from stdin)
- `get_categorized_counts <ref1> <ref2>` - Get category counts
**Usage:**
```bash
source file_operations.sh
counts=$(count_file_operations "main" "feature-branch")
IFS=$'\t' read -r added modified deleted renamed copied <<< "$counts"
echo "Added: $added, Modified: $modified"
```
### utils.sh
General utility functions.
**Functions:**
- `get_current_branch()` - Get current branch name
- `get_default_branch()` - Get default branch (main/master)
- `format_diff_filename <hash1> <hash2>` - Format output filename
- `get_repo_root()` - Get repository root path
- `get_repo_name()` - Get repository name
- `get_remote_url [remote]` - Get remote URL
- `is_ancestor <ref1> <ref2>` - Check if ref1 is ancestor of ref2
- `get_common_ancestor <ref1> <ref2>` - Get merge base
- `format_timestamp [format]` - Format current timestamp
- `ensure_directory <path>` - Create directory if needed
- `get_git_config <key>` - Get git config value
- `is_working_tree_clean()` - Check for uncommitted changes
- `get_branches [type]` - List branches (local/remote/all)
- `ref_exists <ref>` - Check if ref exists
**Usage:**
```bash
source utils.sh
branch=$(get_current_branch)
filename=$(format_diff_filename "abc123" "def456")
```
## Running Scripts Directly
All scripts can be sourced for their functions or run directly for examples:
```bash
# Run directly for examples
./commit_info.sh HEAD
./diff_stats.sh HEAD^ HEAD
./file_operations.sh main feature-branch
./utils.sh
# Source for functions
source diff_stats.sh
if is_large_diff "HEAD^" "HEAD" 500; then
echo "Large diff detected!"
fi
```
## Complete Example
```bash
#!/usr/bin/env bash
# Source all helper scripts
SCRIPT_DIR="$(dirname "${BASH_SOURCE[0]}")"
source "${SCRIPT_DIR}/validate.sh"
source "${SCRIPT_DIR}/commit_info.sh"
source "${SCRIPT_DIR}/diff_stats.sh"
source "${SCRIPT_DIR}/file_operations.sh"
source "${SCRIPT_DIR}/utils.sh"
# Validate we're in a git repo
if ! is_git_repo; then
echo "Error: Not in a git repository"
exit 1
fi
# Get commits to compare
commit1="HEAD^"
commit2="HEAD"
# Validate commits
if ! validate_commit_pair "$commit1" "$commit2" >/dev/null; then
echo "Error: Invalid commits"
exit 1
fi
# Get commit info
info1=$(get_commit_info "$commit1")
info2=$(get_commit_info "$commit2")
IFS='|' read -r _ short1 _ _ _ msg1 <<< "$info1"
IFS='|' read -r _ short2 _ _ _ msg2 <<< "$info2"
echo "Comparing: $short1$short2"
# Get statistics
stats=$(get_diff_stats "$commit1" "$commit2")
IFS=$'\t' read -r files ins del net <<< "$stats"
echo "Files: $files"
echo "Changes: +$ins -$del (net: $net)"
# Check if large
if is_large_diff "$commit1" "$commit2"; then
total=$(is_large_diff "$commit1" "$commit2")
echo "⚠ Large diff: $total lines"
fi
# File operations
ops=$(count_file_operations "$commit1" "$commit2")
IFS=$'\t' read -r added modified deleted renamed _ <<< "$ops"
echo "Added: $added, Modified: $modified"
if [[ $renamed -gt 0 ]]; then
echo "Renamed files:"
get_renamed_files "$commit1" "$commit2" | while IFS=$'\t' read -r sim old new; do
echo " $old$new ($sim%)"
done
fi
# Generate filename
filename=$(format_diff_filename "$short1" "$short2")
echo "Output: $filename"
```
## Error Handling
All scripts use `set -euo pipefail` for strict error handling:
- `-e`: Exit on error
- `-u`: Exit on undefined variable
- `-o pipefail`: Fail on pipe errors
When sourcing, you may want to disable this:
```bash
set +euo pipefail
source diff_stats.sh
set -euo pipefail
```
## Dependencies
- bash 4.0+
- git 2.0+
- Standard Unix utilities (awk, sed, cut, wc)
## Testing
Each script includes a main section that runs when executed directly:
```bash
# Test all scripts
./validate.sh HEAD
./commit_info.sh HEAD HEAD^
./diff_stats.sh HEAD^ HEAD
./file_operations.sh HEAD^ HEAD
./utils.sh
```
## Integration with Skill
These scripts are designed to be used by Claude Code when executing the get-git-diff skill. They provide reliable, composable functions for git diff analysis.
Example integration:
```bash
# In skill execution
source scripts/validate.sh
source scripts/diff_stats.sh
if validate_commit "$user_commit"; then
stats=$(get_diff_stats "$user_commit" "HEAD")
# Process stats...
fi
```

View File

@@ -0,0 +1,157 @@
#!/usr/bin/env bash
#
# Git Commit Information Functions
#
# Functions for retrieving commit metadata and relationships.
#
# Author: Claude Code get-git-diff skill
# Version: 1.0.0
set -euo pipefail
#######################################
# Get detailed information about a commit
# Arguments:
# $1 - commit reference
# Outputs:
# Pipe-separated: full_hash|short_hash|author_name|author_email|date|message
#######################################
get_commit_info() {
local commit_ref="$1"
local format="%H|%h|%an|%ae|%ad|%s"
git log -1 --format="${format}" "${commit_ref}" 2>/dev/null
}
#######################################
# Get short hash from commit reference
# Arguments:
# $1 - commit reference
# $2 - length (optional, default: 7)
# Outputs:
# Short hash
#######################################
get_short_hash() {
local commit_ref="$1"
local length="${2:-7}"
git rev-parse --short="${length}" "${commit_ref}" 2>/dev/null
}
#######################################
# Check if a commit is a merge commit
# Arguments:
# $1 - commit reference
# Returns:
# 0 if merge commit, 1 if not
# Outputs:
# Parent hashes separated by spaces
#######################################
is_merge_commit() {
local commit_ref="$1"
local parents
if parents=$(git log -1 --format="%P" "${commit_ref}" 2>/dev/null); then
local parent_count=$(echo "${parents}" | wc -w)
if [[ ${parent_count} -gt 1 ]]; then
echo "${parents}"
return 0
fi
fi
return 1
}
#######################################
# Get number of commits between two refs
# Arguments:
# $1 - first commit (older)
# $2 - second commit (newer)
# Outputs:
# Number of commits between them
#######################################
get_commits_between() {
local commit1="$1"
local commit2="$2"
git rev-list --count "${commit1}..${commit2}" 2>/dev/null || echo "0"
}
#######################################
# Get commit message (first line)
# Arguments:
# $1 - commit reference
# Outputs:
# Commit message subject line
#######################################
get_commit_message() {
local commit_ref="$1"
git log -1 --format="%s" "${commit_ref}" 2>/dev/null
}
#######################################
# Get commit author
# Arguments:
# $1 - commit reference
# Outputs:
# Author name and email
#######################################
get_commit_author() {
local commit_ref="$1"
git log -1 --format="%an <%ae>" "${commit_ref}" 2>/dev/null
}
#######################################
# Get commit date
# Arguments:
# $1 - commit reference
# Outputs:
# Commit date
#######################################
get_commit_date() {
local commit_ref="$1"
git log -1 --format="%ad" "${commit_ref}" 2>/dev/null
}
# Allow script to be sourced or run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Example usage
if [[ $# -eq 0 ]]; then
echo "Usage: $0 <commit-ref> [<commit-ref2>]"
echo ""
echo "Examples:"
echo " $0 HEAD"
echo " $0 HEAD HEAD^"
exit 1
fi
commit1="$1"
commit2="${2:-}"
echo "=== Commit Info for ${commit1} ==="
info=$(get_commit_info "${commit1}")
IFS='|' read -r full short author email date message <<< "${info}"
echo "Full hash: ${full}"
echo "Short hash: ${short}"
echo "Author: ${author} <${email}>"
echo "Date: ${date}"
echo "Message: ${message}"
if is_merge_commit "${commit1}"; then
parents=$(is_merge_commit "${commit1}")
echo "Merge commit: yes"
echo "Parents: ${parents}"
else
echo "Merge commit: no"
fi
if [[ -n "${commit2}" ]]; then
echo ""
echo "=== Comparison: ${commit1} to ${commit2} ==="
count=$(get_commits_between "${commit1}" "${commit2}")
echo "Commits between: ${count}"
fi
fi

View File

@@ -0,0 +1,178 @@
#!/usr/bin/env bash
#
# Git Diff Statistics Functions
#
# Functions for calculating and analyzing diff statistics.
#
# Author: Claude Code get-git-diff skill
# Version: 1.0.0
set -euo pipefail
#######################################
# Get diff statistics between two commits
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Tab-separated: files_changed insertions deletions net_change
#######################################
get_diff_stats() {
local commit1="$1"
local commit2="$2"
local shortstat
if shortstat=$(git diff --shortstat "${commit1}...${commit2}" 2>/dev/null); then
local files=0 insertions=0 deletions=0
# Parse: "3 files changed, 165 insertions(+), 20 deletions(-)"
if [[ ${shortstat} =~ ([0-9]+)\ files?\ changed ]]; then
files="${BASH_REMATCH[1]}"
fi
if [[ ${shortstat} =~ ([0-9]+)\ insertions? ]]; then
insertions="${BASH_REMATCH[1]}"
fi
if [[ ${shortstat} =~ ([0-9]+)\ deletions? ]]; then
deletions="${BASH_REMATCH[1]}"
fi
local net_change=$((insertions - deletions))
echo "${files} ${insertions} ${deletions} ${net_change}"
else
echo "0 0 0 0"
fi
}
#######################################
# Get detailed per-file statistics
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Tab-separated: insertions deletions filename (one per line)
#######################################
get_file_stats() {
local commit1="$1"
local commit2="$2"
git diff --numstat "${commit1}...${commit2}" 2>/dev/null
}
#######################################
# Check if diff is large (exceeds threshold)
# Arguments:
# $1 - first commit
# $2 - second commit
# $3 - threshold (optional, default: 1000)
# Returns:
# 0 if large, 1 if not
# Outputs:
# Total line count if large
#######################################
is_large_diff() {
local commit1="$1"
local commit2="$2"
local threshold="${3:-1000}"
local stats
if stats=$(get_diff_stats "${commit1}" "${commit2}"); then
local insertions deletions total_lines
insertions=$(echo "${stats}" | cut -f2)
deletions=$(echo "${stats}" | cut -f3)
total_lines=$((insertions + deletions))
if [[ ${total_lines} -gt ${threshold} ]]; then
echo "${total_lines}"
return 0
fi
fi
return 1
}
#######################################
# Get total line count in diff
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Total lines changed (insertions + deletions)
#######################################
get_total_lines() {
local commit1="$1"
local commit2="$2"
local stats
if stats=$(get_diff_stats "${commit1}" "${commit2}"); then
local insertions deletions
insertions=$(echo "${stats}" | cut -f2)
deletions=$(echo "${stats}" | cut -f3)
echo $((insertions + deletions))
else
echo "0"
fi
}
#######################################
# Get files changed count
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Number of files changed
#######################################
get_files_changed() {
local commit1="$1"
local commit2="$2"
local stats
if stats=$(get_diff_stats "${commit1}" "${commit2}"); then
echo "${stats}" | cut -f1
else
echo "0"
fi
}
# Allow script to be sourced or run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Example usage
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <commit1> <commit2> [threshold]"
echo ""
echo "Examples:"
echo " $0 HEAD^ HEAD"
echo " $0 main feature-branch 500"
exit 1
fi
commit1="$1"
commit2="$2"
threshold="${3:-1000}"
echo "=== Diff Statistics: ${commit1}${commit2} ==="
stats=$(get_diff_stats "${commit1}" "${commit2}")
IFS=$'\t' read -r files ins del net <<< "${stats}"
echo "Files changed: ${files}"
echo "Insertions: +${ins}"
echo "Deletions: -${del}"
echo "Net change: ${net}"
echo "Total lines: $((ins + del))"
if is_large_diff "${commit1}" "${commit2}" "${threshold}"; then
total=$(is_large_diff "${commit1}" "${commit2}" "${threshold}")
echo "⚠ Large diff detected (${total} lines, threshold: ${threshold})"
else
echo "✓ Normal size diff (threshold: ${threshold})"
fi
echo ""
echo "=== Top 10 Changed Files ==="
get_file_stats "${commit1}" "${commit2}" | head -10 | while IFS=$'\t' read -r ins del file; do
if [[ "${ins}" == "-" && "${del}" == "-" ]]; then
echo " ${file} (binary)"
else
echo " ${file} (+${ins}, -${del})"
fi
done
fi

View File

@@ -0,0 +1,250 @@
#!/usr/bin/env bash
#
# Git File Operations Functions
#
# Functions for analyzing file operations in diffs (add, modify, delete, rename).
#
# Author: Claude Code get-git-diff skill
# Version: 1.0.0
set -euo pipefail
#######################################
# Get file operations (add, modify, delete, rename)
# Arguments:
# $1 - first commit
# $2 - second commit
# $3 - detect renames (optional, default: true)
# Outputs:
# File operations, one per line (status TAB path)
#######################################
get_file_operations() {
local commit1="$1"
local commit2="$2"
local detect_renames="${3:-true}"
local cmd="git diff --name-status"
if [[ "${detect_renames}" == "true" ]]; then
cmd="${cmd} -M"
fi
${cmd} "${commit1}...${commit2}" 2>/dev/null
}
#######################################
# Count file operations by type
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Tab-separated: added modified deleted renamed copied
#######################################
count_file_operations() {
local commit1="$1"
local commit2="$2"
local operations
operations=$(get_file_operations "${commit1}" "${commit2}")
local added=0 modified=0 deleted=0 renamed=0 copied=0
while IFS= read -r line; do
[[ -z "${line}" ]] && continue
local status="${line:0:1}"
case "${status}" in
A) ((added++)) ;;
M) ((modified++)) ;;
D) ((deleted++)) ;;
R) ((renamed++)) ;;
C) ((copied++)) ;;
esac
done <<< "${operations}"
echo "${added} ${modified} ${deleted} ${renamed} ${copied}"
}
#######################################
# Get files by operation type
# Arguments:
# $1 - first commit
# $2 - second commit
# $3 - operation type (A, M, D, R, C)
# Outputs:
# List of files, one per line
#######################################
get_files_by_operation() {
local commit1="$1"
local commit2="$2"
local operation_type="$3"
local operations
operations=$(get_file_operations "${commit1}" "${commit2}")
echo "${operations}" | awk -v op="${operation_type}" '$1 ~ "^"op {print $2}'
}
#######################################
# Get renamed files with old and new paths
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Tab-separated: similarity old_path new_path (one per line)
#######################################
get_renamed_files() {
local commit1="$1"
local commit2="$2"
local operations
operations=$(get_file_operations "${commit1}" "${commit2}" true)
echo "${operations}" | awk '
/^R[0-9]+/ {
similarity = substr($1, 2)
old_path = $2
new_path = $3
print similarity "\t" old_path "\t" new_path
}
'
}
#######################################
# Get binary files in diff
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# List of binary file paths, one per line
#######################################
get_binary_files() {
local commit1="$1"
local commit2="$2"
local numstat
if numstat=$(git diff --numstat "${commit1}...${commit2}" 2>/dev/null); then
echo "${numstat}" | awk '$1 == "-" && $2 == "-" {print $3}'
fi
}
#######################################
# Count binary files
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Number of binary files
#######################################
count_binary_files() {
local commit1="$1"
local commit2="$2"
get_binary_files "${commit1}" "${commit2}" | wc -l | tr -d ' '
}
#######################################
# Categorize files by type
# Arguments:
# Reads file paths from stdin, one per line
# Outputs:
# Tab-separated: source tests docs config database other
#######################################
categorize_files() {
local source=0 tests=0 docs=0 config=0 database=0 other=0
while IFS= read -r path; do
[[ -z "${path}" ]] && continue
# Categorize based on path and extension
if [[ "${path}" =~ test|tests/ ]]; then
((tests++))
elif [[ "${path}" =~ \.md$|doc ]]; then
((docs++))
elif [[ "${path}" =~ config|settings|\.env|\.ya?ml$|\.json$|\.toml$|\.ini$ ]]; then
((config++))
elif [[ "${path}" =~ migration|schema|models ]]; then
((database++))
elif [[ "${path}" =~ \.(py|js|ts|java|go|rs|cpp|c|rb|php|swift|kt)$ ]]; then
((source++))
else
((other++))
fi
done
echo "${source} ${tests} ${docs} ${config} ${database} ${other}"
}
#######################################
# Get categorized file counts from diff
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Tab-separated category counts
#######################################
get_categorized_counts() {
local commit1="$1"
local commit2="$2"
get_file_operations "${commit1}" "${commit2}" | cut -f2 | categorize_files
}
# Allow script to be sourced or run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Example usage
if [[ $# -lt 2 ]]; then
echo "Usage: $0 <commit1> <commit2>"
echo ""
echo "Examples:"
echo " $0 HEAD^ HEAD"
echo " $0 main feature-branch"
exit 1
fi
commit1="$1"
commit2="$2"
echo "=== File Operations: ${commit1}${commit2} ==="
counts=$(count_file_operations "${commit1}" "${commit2}")
IFS=$'\t' read -r added modified deleted renamed copied <<< "${counts}"
echo "Added: ${added}"
echo "Modified: ${modified}"
echo "Deleted: ${deleted}"
echo "Renamed: ${renamed}"
echo "Copied: ${copied}"
binary_count=$(count_binary_files "${commit1}" "${commit2}")
echo "Binary: ${binary_count}"
if [[ ${added} -gt 0 ]]; then
echo ""
echo "=== Added Files ==="
get_files_by_operation "${commit1}" "${commit2}" "A" | head -10
if [[ ${added} -gt 10 ]]; then
echo " ... and $((added - 10)) more"
fi
fi
if [[ ${renamed} -gt 0 ]]; then
echo ""
echo "=== Renamed Files ==="
get_renamed_files "${commit1}" "${commit2}" | head -10 | while IFS=$'\t' read -r sim old new; do
echo " ${old}${new} (${sim}%)"
done
if [[ ${renamed} -gt 10 ]]; then
echo " ... and $((renamed - 10)) more"
fi
fi
echo ""
echo "=== File Categorization ==="
categories=$(get_categorized_counts "${commit1}" "${commit2}")
IFS=$'\t' read -r source tests docs config database other <<< "${categories}"
echo "Source code: ${source}"
echo "Tests: ${tests}"
echo "Documentation: ${docs}"
echo "Configuration: ${config}"
echo "Database: ${database}"
echo "Other: ${other}"
fi

View File

@@ -0,0 +1,250 @@
#!/usr/bin/env bash
#
# Git Utility Functions
#
# General utility functions for git operations and formatting.
#
# Author: Claude Code get-git-diff skill
# Version: 1.0.0
set -euo pipefail
#######################################
# Get current branch name
# Outputs:
# Branch name, or "HEAD" if detached
#######################################
get_current_branch() {
local branch
branch=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "HEAD")
echo "${branch}"
}
#######################################
# Get default branch (main or master)
# Outputs:
# Default branch name, or empty if not found
#######################################
get_default_branch() {
if git rev-parse --verify main >/dev/null 2>&1; then
echo "main"
elif git rev-parse --verify master >/dev/null 2>&1; then
echo "master"
else
echo ""
fi
}
#######################################
# Format diff filename
# Arguments:
# $1 - first commit short hash
# $2 - second commit short hash
# $3 - extension (optional, default: md)
# Outputs:
# Formatted filename like "diff_abc123_def456.md"
#######################################
format_diff_filename() {
local hash1="$1"
local hash2="$2"
local ext="${3:-md}"
echo "diff_${hash1}_${hash2}.${ext}"
}
#######################################
# Get repository root directory
# Outputs:
# Absolute path to repository root
#######################################
get_repo_root() {
git rev-parse --show-toplevel 2>/dev/null
}
#######################################
# Get repository name
# Outputs:
# Name of the repository (directory name)
#######################################
get_repo_name() {
local root
root=$(get_repo_root)
if [[ -n "${root}" ]]; then
basename "${root}"
fi
}
#######################################
# Get remote URL (if available)
# Arguments:
# $1 - remote name (optional, default: origin)
# Outputs:
# Remote URL or empty if not found
#######################################
get_remote_url() {
local remote="${1:-origin}"
git remote get-url "${remote}" 2>/dev/null || echo ""
}
#######################################
# Check if commit is reachable from another
# Arguments:
# $1 - ancestor commit
# $2 - descendant commit
# Returns:
# 0 if reachable, 1 if not
#######################################
is_ancestor() {
local ancestor="$1"
local descendant="$2"
git merge-base --is-ancestor "${ancestor}" "${descendant}" 2>/dev/null
}
#######################################
# Get common ancestor of two commits
# Arguments:
# $1 - first commit
# $2 - second commit
# Outputs:
# Common ancestor commit hash
#######################################
get_common_ancestor() {
local commit1="$1"
local commit2="$2"
git merge-base "${commit1}" "${commit2}" 2>/dev/null
}
#######################################
# Format timestamp
# Arguments:
# $1 - format (optional, default: "%Y-%m-%d %H:%M:%S")
# Outputs:
# Formatted current timestamp
#######################################
format_timestamp() {
local format="${1:-%Y-%m-%d %H:%M:%S}"
date "+${format}"
}
#######################################
# Create directory if it doesn't exist
# Arguments:
# $1 - directory path
# Returns:
# 0 if successful or already exists
#######################################
ensure_directory() {
local dir="$1"
if [[ ! -d "${dir}" ]]; then
mkdir -p "${dir}"
fi
}
#######################################
# Get git config value
# Arguments:
# $1 - config key (e.g., user.name)
# Outputs:
# Config value or empty if not set
#######################################
get_git_config() {
local key="$1"
git config --get "${key}" 2>/dev/null || echo ""
}
#######################################
# Check if working directory is clean
# Returns:
# 0 if clean, 1 if dirty
#######################################
is_working_tree_clean() {
git diff-index --quiet HEAD -- 2>/dev/null
}
#######################################
# Get list of all branches
# Arguments:
# $1 - type: "local", "remote", or "all" (default: local)
# Outputs:
# List of branches, one per line
#######################################
get_branches() {
local type="${1:-local}"
case "${type}" in
local)
git branch --format='%(refname:short)' 2>/dev/null
;;
remote)
git branch -r --format='%(refname:short)' 2>/dev/null
;;
all)
git branch -a --format='%(refname:short)' 2>/dev/null
;;
*)
echo "Error: Invalid branch type: ${type}" >&2
return 1
;;
esac
}
#######################################
# Check if ref exists
# Arguments:
# $1 - ref name (branch, tag, commit)
# Returns:
# 0 if exists, 1 if not
#######################################
ref_exists() {
local ref="$1"
git rev-parse --verify "${ref}" >/dev/null 2>&1
}
# Allow script to be sourced or run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Example usage
echo "=== Git Repository Utilities ==="
echo ""
echo "Current branch: $(get_current_branch)"
echo "Default branch: $(get_default_branch)"
echo "Repository root: $(get_repo_root)"
echo "Repository name: $(get_repo_name)"
remote_url=$(get_remote_url)
if [[ -n "${remote_url}" ]]; then
echo "Remote URL (origin): ${remote_url}"
fi
echo ""
echo "Git user name: $(get_git_config user.name)"
echo "Git user email: $(get_git_config user.email)"
echo ""
if is_working_tree_clean; then
echo "Working tree: clean"
else
echo "Working tree: dirty (uncommitted changes)"
fi
echo ""
echo "Example filename: $(format_diff_filename "abc123" "def456")"
echo "Current timestamp: $(format_timestamp)"
echo ""
echo "=== Local Branches ==="
get_branches local | head -5
local_count=$(get_branches local | wc -l)
if [[ ${local_count} -gt 5 ]]; then
echo " ... and $((local_count - 5)) more"
fi
fi

View File

@@ -0,0 +1,88 @@
#!/usr/bin/env bash
#
# Git Validation Functions
#
# Functions for validating git repositories and commit references.
#
# Author: Claude Code get-git-diff skill
# Version: 1.0.0
set -euo pipefail
#######################################
# Check if in a git repository
# Returns:
# 0 if in git repo, 1 if not
#######################################
is_git_repo() {
git rev-parse --is-inside-work-tree >/dev/null 2>&1
}
#######################################
# Validate that a commit reference exists
# Arguments:
# $1 - commit reference (hash, branch, tag)
# Returns:
# 0 if valid, 1 if invalid
# Outputs:
# Full commit hash if valid, error message to stderr if invalid
#######################################
validate_commit() {
local commit_ref="$1"
local full_hash
if full_hash=$(git rev-parse --verify "${commit_ref}" 2>/dev/null); then
echo "${full_hash}"
return 0
else
echo "Error: Invalid commit reference: ${commit_ref}" >&2
return 1
fi
}
#######################################
# Validate two commit references
# Arguments:
# $1 - first commit reference
# $2 - second commit reference
# Returns:
# 0 if both valid, 1 if either invalid
# Outputs:
# Tab-separated full hashes if valid
#######################################
validate_commit_pair() {
local commit1="$1"
local commit2="$2"
local hash1 hash2
if hash1=$(validate_commit "${commit1}") && hash2=$(validate_commit "${commit2}"); then
echo "${hash1} ${hash2}"
return 0
else
return 1
fi
}
# Allow script to be sourced or run directly
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
# Example usage
if [[ $# -eq 0 ]]; then
echo "Usage: $0 <commit-ref> [<commit-ref2>]"
echo ""
echo "Examples:"
echo " $0 HEAD"
echo " $0 abc123 def456"
exit 1
fi
if is_git_repo; then
if [[ $# -eq 1 ]]; then
validate_commit "$1"
else
validate_commit_pair "$1" "$2"
fi
else
echo "Error: Not in a git repository" >&2
exit 1
fi
fi

View File

@@ -0,0 +1,241 @@
# Git Diff: [SHORT_HASH_1] → [SHORT_HASH_2]
**Generated**: [YYYY-MM-DD HH:MM:SS]
**Skill**: get-git-diff v1.0.0
---
## Commit Information
### From Commit
- **Hash**: `[FULL_HASH_1]` ([SHORT_HASH_1])
- **Author**: [AUTHOR_NAME] <[AUTHOR_EMAIL]>
- **Date**: [COMMIT_DATE_1]
- **Message**: [COMMIT_MESSAGE_1]
### To Commit
- **Hash**: `[FULL_HASH_2]` ([SHORT_HASH_2])
- **Author**: [AUTHOR_NAME] <[AUTHOR_EMAIL]>
- **Date**: [COMMIT_DATE_2]
- **Message**: [COMMIT_MESSAGE_2]
### Comparison Details
- **Commits Between**: [N] commits
- **Branch Context**: [BRANCH_INFO] (if applicable)
- **Merge Commit**: [Yes/No] (if applicable)
---
## Summary
### Change Statistics
| Metric | Count |
|--------|-------|
| Files Changed | [N] |
| Insertions | [N] (+) |
| Deletions | [N] (-) |
| Net Change | [±N] |
### File Operations
#### Added Files ([N])
[If any files were added, list them here]
- `path/to/new_file.py`
- `path/to/another_file.js`
#### Modified Files ([N])
[If any files were modified, list them here]
- `src/module.py` (+[N], -[N])
- `tests/test_module.py` (+[N], -[N])
#### Deleted Files ([N])
[If any files were deleted, list them here]
- `old/deprecated_file.py`
#### Renamed Files ([N])
[If any files were renamed, list them here]
- `old/path/file.py``new/path/file.py` ([SIMILARITY]%)
#### Binary Files ([N])
[If any binary files changed, list them here]
- `static/images/logo.png` (binary)
- `docs/manual.pdf` (binary)
---
## Change Categorization
### By File Type
| Category | Files | Insertions | Deletions |
|----------|-------|------------|-----------|
| Source Code | [N] | [N] | [N] |
| Tests | [N] | [N] | [N] |
| Documentation | [N] | [N] | [N] |
| Configuration | [N] | [N] | [N] |
| Database | [N] | [N] | [N] |
| Other | [N] | [N] | [N] |
### Change Type Assessment
[Based on patterns identified]
- **Primary Type**: [Feature Addition / Bug Fix / Refactoring / etc.]
- **Impact Areas**:
- [Area 1 - e.g., Authentication system]
- [Area 2 - e.g., User API endpoints]
- [Area 3 - e.g., Test coverage]
### Risk Assessment
[Based on change patterns and areas affected]
- **Risk Level**: [Low / Medium / High / Critical]
- **Risk Factors**:
- [Factor 1 - e.g., Database schema changes]
- [Factor 2 - e.g., Breaking API changes]
- [Factor 3 - e.g., Security-sensitive code modified]
---
## Special Notes
[Include any special considerations or warnings]
### Large Diff Warning
[If applicable]
⚠️ **This diff contains [N] lines across [M] files.** This is a substantial change set.
[If summary only: "Full unified diff omitted due to size. Use `git diff [hash1]...[hash2]` to view complete diff."]
### Merge Commit
[If applicable]
⚠️ **This is a merge commit** merging [BRANCH_NAME] into [BASE_BRANCH].
- Parent 1: [HASH] ([BRANCH])
- Parent 2: [HASH] ([BRANCH])
### Binary Changes
[If applicable]
⚠️ **This diff includes [N] binary files.** Binary content is not displayed in the diff below.
### Renamed Files
[If applicable]
**[N] files were renamed or moved.** Old and new paths are shown above with similarity percentages.
---
## Detailed Diff
[If full diff is included, otherwise note that it was omitted]
```diff
[FULL_UNIFIED_DIFF_OUTPUT]
Example format:
diff --git a/src/module.py b/src/module.py
index abc123..def456 100644
--- a/src/module.py
+++ b/src/module.py
@@ -10,7 +10,8 @@ def my_function():
# Unchanged line
- old_line = "removed"
+ new_line = "added"
+ another_line = "also added"
# More context
diff --git a/tests/test_module.py b/tests/test_module.py
index 111222..333444 100644
--- a/tests/test_module.py
+++ b/tests/test_module.py
@@ -5,3 +5,6 @@ def test_my_function():
assert my_function() is not None
+
+def test_new_behavior():
+ assert new_line == "added"
```
---
## Recommendations
[Optional section with actionable recommendations based on diff analysis]
### Testing
- [ ] Verify all tests pass with these changes
- [ ] Add tests for any new functionality
- [ ] Check test coverage for modified areas
### Code Review Focus
- [ ] Review [specific area] for correctness
- [ ] Validate [specific concern] is addressed
- [ ] Ensure backward compatibility for [specific component]
### Deployment Considerations
- [ ] Update environment variables if config changed
- [ ] Run database migrations if schema changed
- [ ] Update documentation if API changed
- [ ] Coordinate with [team] if [specific area] changed
---
## Related Commands
```bash
# View this diff locally
git diff [SHORT_HASH_1]...[SHORT_HASH_2]
# View with file names only
git diff --name-status [SHORT_HASH_1]...[SHORT_HASH_2]
# View with statistics
git diff --stat [SHORT_HASH_1]...[SHORT_HASH_2]
# View specific file from this diff
git diff [SHORT_HASH_1]...[SHORT_HASH_2] -- path/to/file
# View commit messages in range
git log --oneline [SHORT_HASH_1]..[SHORT_HASH_2]
```
---
## Metadata
- **Analyzed by**: Claude Code get-git-diff skill v1.0.0
- **Analysis date**: [YYYY-MM-DD HH:MM:SS]
- **Repository**: [REPO_PATH or REPO_URL if available]
- **Output format**: Standard Unified Diff with Summary
---
<!--
Template Usage Instructions:
Replace all placeholders in [BRACKETS] with actual values:
- [SHORT_HASH_1/2] - 7-character commit hash
- [FULL_HASH_1/2] - Full 40-character commit hash
- [AUTHOR_NAME] - Commit author name
- [AUTHOR_EMAIL] - Commit author email
- [COMMIT_DATE_1/2] - Commit date
- [COMMIT_MESSAGE_1/2] - Commit message (first line)
- [N] - Numeric values (file counts, line counts, etc.)
- [BRANCH_INFO] - Branch context information
- [SIMILARITY] - Similarity percentage for renames
- [FULL_UNIFIED_DIFF_OUTPUT] - Complete git diff output
Conditional Sections:
- Include "Large Diff Warning" only if diff > 1000 lines
- Include "Merge Commit" only if commit is a merge
- Include "Binary Changes" only if binary files present
- Include "Renamed Files" only if renames detected
- Omit empty categories (e.g., if no files deleted, omit that section)
Formatting:
- Use proper markdown syntax
- Wrap code/paths in backticks: `path/to/file`
- Use tables for structured data
- Use bullet lists for items
- Use checkboxes [ ] for action items
- Use emoji/symbols sparingly: ⚠️ for warnings, for info
Output Location:
- Save to: /claudedocs/diff_{short_hash1}_{short_hash2}.md
- Ensure /claudedocs directory exists
- Use filesystem-safe characters only
-->