merge-strategy
The merge-strategy skill documents sidecar's git merge and pull operations, covering four pull strategies (merge, rebase, fast-forward only, rebase with autostash), workspace merge workflows for PR and direct merge scenarios, and eight known gaps including lack of configurable defaults, limited merge strategies, missing squash support, and hardcoded commit messages. Use this skill to understand current merge behavior and identify configuration needs for safer or more flexible git integration workflows.
git clone --depth 1 https://github.com/marcus/sidecar /tmp/merge-strategy && cp -r /tmp/merge-strategy/.claude/skills/merge-strategy ~/.claude/skills/merge-strategySKILL.md
# Merge Strategy Configuration
This skill covers sidecar's git merge and pull operations, their current implementation, known gaps, and recommended configuration patterns.
## Current Implementation
### Git Status Plugin Pull Strategies
Location: `internal/plugins/gitstatus/pull_menu.go`, `internal/plugins/gitstatus/remote.go`
The git-status plugin offers four pull strategies via a modal menu:
| Strategy | Command | Use Case |
|----------|---------|----------|
| Pull (merge) | `git pull` | Creates merge commit, preserves branch topology |
| Pull (rebase) | `git pull --rebase` | Replays local commits on top of upstream |
| Pull (fast-forward only) | `git pull --ff-only` | Only pulls if fast-forward possible (safest) |
| Pull (rebase + autostash) | `git pull --rebase --autostash` | Rebase with automatic stash/unstash |
### Workspace Plugin Merge Workflows
Location: `internal/plugins/workspace/merge.go`
**PR Workflow:**
1. Push branch to remote
2. Create PR via `gh pr create`
3. Wait for PR merge (polling)
4. Cleanup: delete worktree, branches, pull base
**Direct Merge (No PR)** at `merge.go:430-498`:
```
1. git fetch origin <baseBranch>
2. git checkout <baseBranch>
3. git pull origin <baseBranch>
4. git merge <branch> --no-ff -m "Merge branch '<branch>'" // Hardcoded --no-ff
5. git push origin <baseBranch>
```
**Post-Merge Pull** at `merge.go:500-551`:
- When on base branch: `git pull --ff-only origin <branch>` (hardcoded)
- Otherwise: `git fetch` + `git update-ref`
**Divergence Resolution** at `merge.go:644-705`:
- Rebase option: `git pull --rebase origin <branch>`
- Merge option: `git pull origin <branch>`
## Known Issues and Gaps
1. **No configurable defaults** -- users must select strategy every time
2. **Limited merge strategies** -- direct merge hardcoded to `--no-ff`, missing `--ff`, `--ff-only`, `--squash`, rebase-based integration
3. **No squash merge support** -- no clean-history option for main branch
4. **Hardcoded commit messages** -- `fmt.Sprintf("Merge branch '%s'", branch)`, no templates
5. **Missing safety options** -- no GPG signing, no `--verify-signatures`, no `--force-with-lease`
6. **Autostash inconsistency** -- available for pull-rebase but not post-merge pull or divergence resolution
7. **No upstream tracking config** -- remote always `origin`, no configurable tracking
8. **Limited conflict recovery** -- only abort or dismiss, no continue/skip rebase
9. **No interactive rebase** -- no squash/reorder/edit before merge
## Recommended Configuration Schema
Add to `internal/config/config.go`:
```go
type GitConfig struct {
DefaultPullStrategy string `json:"defaultPullStrategy,omitempty"` // "merge", "rebase", "ff-only", "autostash"
DefaultMergeStrategy string `json:"defaultMergeStrategy,omitempty"` // "no-ff", "ff", "ff-only", "squash", "rebase"
MergeCommitTemplate string `json:"mergeCommitTemplate,omitempty"` // Go template: .Branch, .BaseBranch, .PRTitle, .PRNumber
SignCommits bool `json:"signCommits,omitempty"`
SignMerges bool `json:"signMerges,omitempty"`
AutostashOnPull bool `json:"autostashOnPull,omitempty"`
DefaultRemote string `json:"defaultRemote,omitempty"` // Default: "origin"
SetUpstreamOnPush bool `json:"setUpstreamOnPush,omitempty"`
ProtectedBranches []string `json:"protectedBranches,omitempty"` // e.g. ["main", "master", "release/*"]
PreMergeChecks []string `json:"preMergeChecks,omitempty"` // e.g. ["go test ./..."]
}
```
Add to `PluginsConfig`:
```go
type PluginsConfig struct {
Git GitConfig `json:"git"`
GitStatus GitStatusPluginConfig `json:"git-status"`
// ... existing fields
}
```
Example user config (`~/.config/sidecar/config.json`):
```json
{
"plugins": {
"git": {
"defaultPullStrategy": "rebase",
"defaultMergeStrategy": "squash",
"mergeCommitTemplate": "Merge {{.Branch}} into {{.BaseBranch}}",
"signCommits": true,
"autostashOnPull": true,
"protectedBranches": ["main", "production"]
}
}
}
```
## Implementation Priorities
### Priority 1: Default Pull Strategy
**Files:** `internal/config/config.go`, `internal/plugins/gitstatus/pull_menu.go`, `internal/plugins/gitstatus/update_handlers.go`
- If `defaultPullStrategy` is set, execute directly (skip menu)
- If unset, show menu as today
- Consider adding "Always use this" option to menu
### Priority 2: Default Merge Strategy
**File:** `internal/plugins/workspace/merge.go` -- `performDirectMerge()`
```go
func (p *Plugin) performDirectMerge(wt *Worktree) tea.Cmd {
strategy := p.ctx.Config.Plugins.Git.DefaultMergeStrategy
if strategy == "" {
strategy = "no-ff" // backward compatible default
}
var mergeArgs []string
switch strategy {
case "ff":
mergeArgs = []string{"merge", branch, "-m", mergeMsg}
case "ff-only":
mergeArgs = []string{"merge", branch, "--ff-only"}
case "squash":
mergeArgs = []string{"merge", branch, "--squash"}
case "rebase":
// 1. git rebase baseBranch branch
// 2. git checkout baseBranch
// 3. git merge branch --ff-only
default: // "no-ff"
mergeArgs = []string{"merge", branch, "--no-ff", "-m", mergeMsg}
}
}
```
### Priority 3: Squash Merge Support
After `git merge --squash`, a separate commit is needed:
```go
case "squash":
mergeCmd := exec.Command("git", "merge", branch, "--squash")
// execute...
commitMsg := fmt.Sprintf("Squash merge branch '%s'", branch)
commitCmd := exec.Command("git", "commit", "-m", commitMsg)
```
### Priority 4: Merge Commit Templates
```go
type MergeTemplateData struct {
Branch, BaseBranch, PRTitle, PRNumber string
}
func renderMergeMessage(tmpl string, data MergeTemplateData) (string, error) {
if tmpl == "" {
tmpl = "Merge branch '{{.Branch}}'"
}
t, err := template.New("merge").Parse(tmpl)>
Create declarative modals using the modal library API. Covers modal types (confirm, input, select, form), sections (Text, Buttons, Input, Textarea, Checkbox, List, When, Custom), rendering with OverlayModal, and keyboard/mouse handling. Use when adding modals or dialogs to the application.
>
Create prompts for sidecar workspaces. Covers prompt structure (name, ticketMode, body), template variables (ticket with fallbacks), config file locations (global vs project), and scope overrides. Use when creating or modifying prompts in sidecar config files.
>
>
Creating and using feature flags in sidecar for gating experimental functionality. Covers flag registration, checking flags in code, config file and CLI overrides, and priority resolution. Use when adding feature flags, toggling features, or gating new functionality behind flags.
>