Skip to content

Commit c0a63d9

Browse files
mtnbikencclaude
andcommitted
Enhance branch-cleanup with multi-layered merge detection
Improves the git:branch-cleanup command to detect merged branches using multiple methods, preventing false negatives when branches are rebased, squashed, or cherry-picked. Key improvements: - Added check_if_merged() function with 4 detection methods: * Standard: git branch --merged (commits in main history) * Merge-commit: searches merge commit messages * Content-identical: compares content with git diff (catches rebases) * Cherry-picked: detects equivalent patches with git cherry - Enhanced categorization to show detection method used - Updated examples to demonstrate new merge status labels - Improved output format with explanatory notes for rebased branches This fixes cases where branches show as unmerged despite having identical content in main, such as when maintainers rebase PRs before merging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent a50fc18 commit c0a63d9

File tree

1 file changed

+66
-6
lines changed

1 file changed

+66
-6
lines changed

plugins/git/commands/branch-cleanup.md

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,20 @@ The command should follow these steps:
3434
2. **Gather Branch Information**
3535
- List all local branches: `git branch`
3636
- Get current branch: `git branch --show-current`
37-
- Identify merged branches: `git branch --merged <main-branch>`
37+
- Identify merged branches using multi-layered detection:
38+
- Standard merge: `git branch --merged <main-branch>`
39+
- Merge commit messages: `git log <main-branch> --merges --oneline`
40+
- Content comparison: `git diff <main-branch>...<branch> --quiet`
41+
- Cherry-pick detection: `git cherry <main-branch> <branch>`
3842
- Check remote tracking: `git branch -vv`
3943
- Find remote-deleted branches: `git remote prune origin --dry-run`
4044

4145
3. **Categorize Branches**
42-
- **Merged branches**: Fully merged into main branch
46+
- **Merged branches**: Detected via one of these methods:
47+
- Standard: Commits directly in main branch history
48+
- Merge commit: Found in main's merge commit messages
49+
- Content-identical: All changes present in main (rebased/cherry-picked)
50+
- Cherry-picked: All commits have equivalents in main
4351
- **Gone branches**: Remote tracking branch no longer exists
4452
- **Stale branches**: Last commit older than threshold (e.g., 3 months)
4553
- **Protected branches**: main, master, develop, release/*, hotfix/*
@@ -79,8 +87,58 @@ fi
7987
# Get current branch
8088
current_branch=$(git branch --show-current)
8189

82-
# Find merged branches
83-
git branch --merged "$main_branch" | grep -v "^\*" | grep -v "$main_branch"
90+
# Multi-layered merge detection function
91+
# Returns: "merged:<method>" or "not-merged"
92+
check_if_merged() {
93+
local branch=$1
94+
local main_branch=$2
95+
96+
# Method 1: Standard merge check (commits in main history)
97+
if git branch --merged "$main_branch" | grep -q "^[* ]*${branch}$"; then
98+
echo "merged:standard"
99+
return 0
100+
fi
101+
102+
# Method 2: Check merge commit messages (catches various merge formats)
103+
# Matches: "Merge branch 'X'", "Merge pull request #X", "Squash merge", etc.
104+
if git log "$main_branch" --merges --oneline | grep -qiE "(merge|squash).*(${branch}|branch[[:space:]]+'${branch}'|pull.?request)"; then
105+
echo "merged:merge-commit"
106+
return 0
107+
fi
108+
109+
# Method 3: Content comparison (handles rebased/cherry-picked branches)
110+
# Uses three-dot syntax (merge-base comparison) to detect content-identical branches
111+
# If diff is empty, all content is in main even if commit hashes differ
112+
# Note: 2>/dev/null suppresses errors for edge cases (no common ancestor, invalid branch)
113+
if git diff --quiet "$main_branch"..."$branch" 2>/dev/null; then
114+
echo "merged:content-identical"
115+
return 0
116+
fi
117+
118+
# Method 4: Cherry-pick detection (all commits have equivalents in main)
119+
# Commits prefixed with '-' have equivalent patches in main
120+
# Note: Detects patch equivalence, not necessarily commits reachable from main
121+
# May have rare false positives with coincidentally similar commits
122+
local unmerged=$(git cherry "$main_branch" "$branch" 2>/dev/null | grep -c '^+')
123+
if [ "$unmerged" -eq 0 ]; then
124+
echo "merged:cherry-picked"
125+
return 0
126+
fi
127+
128+
echo "not-merged"
129+
return 1
130+
}
131+
132+
# Find all merged branches with detection method
133+
for branch in $(git branch | grep -v "^\*" | sed 's/^[ ]*//'); do
134+
if [ "$branch" != "$main_branch" ]; then
135+
merge_status=$(check_if_merged "$branch" "$main_branch")
136+
if [[ "$merge_status" == merged:* ]]; then
137+
method=${merge_status#merged:}
138+
echo "$branch|$method"
139+
fi
140+
fi
141+
done
84142

85143
# Find branches with deleted remotes ("gone")
86144
git branch -vv | grep ': gone]' | awk '{print $1}'
@@ -123,8 +181,9 @@ git remote prune origin
123181
Current branch: feature/new-api
124182
125183
=== Merged Branches (safe to delete) ===
126-
feature/bug-fix-123 Merged 2 weeks ago
127-
feature/update-deps Merged 1 month ago
184+
feature/bug-fix-123 Merged (standard) - 2 weeks ago
185+
feature/update-deps Merged (merge-commit) - 1 month ago
186+
feature/rebased-work Merged (content-identical) - 3 days ago
128187
129188
=== Gone Branches (remote deleted) ===
130189
feature/old-feature Remote: gone
@@ -139,6 +198,7 @@ git remote prune origin
139198
140199
Recommendations:
141200
- Safe to delete: feature/bug-fix-123, feature/update-deps (merged)
201+
* Note: feature/bug-fix-123 has different commits but identical content (rebased)
142202
- Safe to delete: feature/old-feature, hotfix/urgent-fix (remote gone)
143203
- Review needed: experiment/prototype (unmerged, stale)
144204

0 commit comments

Comments
 (0)