184 lines
4.7 KiB
Markdown
184 lines
4.7 KiB
Markdown
```skill
|
|
---
|
|
name: merge-branch
|
|
description: Git branch merging workflow — merges current feature branch into the default branch (main/master) with conflict detection, working tree validation, and safety checks.
|
|
---
|
|
|
|
# Merge Branch
|
|
|
|
## Overview
|
|
|
|
This skill merges the current feature branch into the detected default branch (main or master).
|
|
|
|
**Prerequisites:**
|
|
- Working in a git repository
|
|
- On a feature branch (not already on default branch)
|
|
- Have commits ready to merge
|
|
|
|
**Outcome:**
|
|
- Feature branch merged into default branch
|
|
- Default branch updated with latest changes
|
|
- Ready to push merged changes to remote
|
|
|
|
## Default Branch Detection
|
|
|
|
Detect the default branch name:
|
|
```sh
|
|
git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@'
|
|
```
|
|
Fallback: check if `master` exists, else `main`. Store as `{default_branch}`.
|
|
|
|
## Pre-Merge Validation
|
|
|
|
Run these checks BEFORE merging:
|
|
|
|
### 1. Get Current Branch
|
|
|
|
```sh
|
|
CURRENT_BRANCH=$(git branch --show-current)
|
|
```
|
|
|
|
### 2. Verify Not on Default Branch
|
|
|
|
```sh
|
|
if [ "$CURRENT_BRANCH" = "{default_branch}" ]; then
|
|
echo "ERROR: Already on default branch. Cannot merge into self."
|
|
exit 1
|
|
fi
|
|
```
|
|
|
|
### 3. Check Working Tree State
|
|
|
|
```sh
|
|
IS_DIRTY=$(git status --porcelain)
|
|
```
|
|
|
|
If `IS_DIRTY` is not empty, working tree is dirty. Handle before merging.
|
|
|
|
## Dirty Working Tree Options
|
|
|
|
When dirty, present these options to the user (use `ask_questions` tool):
|
|
|
|
- **Commit changes first**: `git add -A && git commit -m "message"` — commits all changes before merging
|
|
- **Stash changes**: `git stash push -m "WIP: auto-stash before merge"` — saves changes for later
|
|
- **Discard changes**: `git checkout -- . && git clean -fd` — permanently removes uncommitted changes
|
|
- **Abort**: Stop the workflow — user handles git state manually
|
|
|
|
After committing, stashing, or discarding, working tree is clean. Proceed with merge.
|
|
|
|
## Merge Execution
|
|
|
|
After working tree is clean:
|
|
|
|
### 1. Switch to Default Branch
|
|
|
|
```sh
|
|
git checkout {default_branch}
|
|
```
|
|
|
|
### 2. Pull Latest Changes
|
|
|
|
```sh
|
|
git pull
|
|
```
|
|
|
|
### 3. Execute Merge
|
|
|
|
```sh
|
|
git merge $CURRENT_BRANCH
|
|
```
|
|
|
|
### 4. Handle Merge Result
|
|
|
|
Check exit code:
|
|
|
|
- **Exit 0** (success): Merge completed successfully
|
|
```
|
|
✓ Merged $CURRENT_BRANCH into {default_branch}
|
|
Next steps:
|
|
- Review merged changes: git log
|
|
- Push to remote: git push
|
|
- Delete feature branch if done: git branch -d $CURRENT_BRANCH
|
|
```
|
|
|
|
- **Non-zero exit** (conflict): Merge conflict detected
|
|
```sh
|
|
git merge --abort
|
|
echo "ERROR: Merge conflict detected. Merge aborted."
|
|
echo "Resolve conflicts manually and retry merge."
|
|
exit 1
|
|
```
|
|
|
|
## Error Handling
|
|
|
|
| Scenario | Error Message | Exit Code |
|
|
|----------|---------------|-----------|
|
|
| Already on default branch | `ERROR: Already on default branch. Cannot merge into self.` | 1 |
|
|
| Dirty working tree (user aborts) | `ERROR: Working tree is dirty. Merge aborted.` | 1 |
|
|
| Merge conflict | `ERROR: Merge conflict detected. Merge aborted.` | 1 |
|
|
| Default branch detection fails | `ERROR: Could not detect default branch.` | 1 |
|
|
|
|
All errors exit with non-zero status to prevent downstream issues.
|
|
|
|
## Usage Example
|
|
|
|
Complete workflow demonstrating all steps:
|
|
|
|
```sh
|
|
#!/bin/bash
|
|
set -e
|
|
|
|
# Step 1: Detect default branch
|
|
DEFAULT_BRANCH=$(git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@')
|
|
if [ -z "$DEFAULT_BRANCH" ]; then
|
|
if git show-ref --verify --quiet refs/heads/master; then
|
|
DEFAULT_BRANCH="master"
|
|
else
|
|
DEFAULT_BRANCH="main"
|
|
fi
|
|
fi
|
|
|
|
# Step 2: Get current branch
|
|
CURRENT_BRANCH=$(git branch --show-current)
|
|
|
|
# Step 3: Verify not on default branch
|
|
if [ "$CURRENT_BRANCH" = "$DEFAULT_BRANCH" ]; then
|
|
echo "ERROR: Already on default branch. Cannot merge into self."
|
|
exit 1
|
|
fi
|
|
|
|
# Step 4: Check working tree state
|
|
IS_DIRTY=$(git status --porcelain)
|
|
if [ -n "$IS_DIRTY" ]; then
|
|
echo "WARNING: Working tree is dirty."
|
|
echo "Options: (c)ommit, (s)tash, (d)iscard, (a)bort"
|
|
read -p "Choose: " choice
|
|
case $choice in
|
|
c) git add -A && git commit -m "Changes before merge" ;;
|
|
s) git stash push -m "WIP: auto-stash before merge" ;;
|
|
d) git checkout -- . && git clean -fd ;;
|
|
a) echo "Merge aborted."; exit 1 ;;
|
|
*) echo "Invalid choice. Aborting."; exit 1 ;;
|
|
esac
|
|
fi
|
|
|
|
# Step 5: Switch to default branch
|
|
git checkout $DEFAULT_BRANCH
|
|
|
|
# Step 6: Pull latest changes
|
|
git pull
|
|
|
|
# Step 7: Execute merge
|
|
if git merge $CURRENT_BRANCH; then
|
|
echo "✓ Merged $CURRENT_BRANCH into $DEFAULT_BRANCH"
|
|
echo "Next steps:"
|
|
echo "- Review merged changes: git log"
|
|
echo "- Push to remote: git push"
|
|
echo "- Delete feature branch if done: git branch -d $CURRENT_BRANCH"
|
|
else
|
|
echo "ERROR: Merge conflict detected. Aborting merge."
|
|
git merge --abort
|
|
exit 1
|
|
fi
|
|
```
|