From 5170bd1a7dc2b38addbfec57720dc1f73db434e3 Mon Sep 17 00:00:00 2001 From: Jakub Czajka Date: Mon, 6 Jul 2026 11:24:17 +0200 Subject: [PATCH] [ai] Add merge-worktree workflow: branch-summary, merge script, and skill doc. Introduces a workflow for merging feature branches into main via rebase + fast-forward merge. The skill document teaches Claude the merge-worktree procedure so it can be invoked as a slash command. --- .claude/scripts/branch-summary | 46 ++++++++++++++++++ .claude/scripts/merge-worktree | 82 ++++++++++++++++++++++++++++++++ .claude/skills/merge-worktree.md | 48 +++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100755 .claude/scripts/branch-summary create mode 100755 .claude/scripts/merge-worktree create mode 100644 .claude/skills/merge-worktree.md diff --git a/.claude/scripts/branch-summary b/.claude/scripts/branch-summary new file mode 100755 index 0000000..0623773 --- /dev/null +++ b/.claude/scripts/branch-summary @@ -0,0 +1,46 @@ +#!/bin/sh +# Copyright (c) 2026 Jakub Czajka +# License: GPL-3.0 or later. +# +# branch-summary — Summarize non-main branches as a markdown table. + +# List all local branches except main. +branches=$($HOME/.guix-home/profile/bin/git \ + -C "$CLAUDE_PROJECT_DIR" for-each-ref \ + --format='%(refname:short)' refs/heads/ \ + | $GUIX_BIN/grep -v '^main$') + +if [ -z "$branches" ]; then + $GUIX_BIN/echo "No other branches." + exit 0 +fi + +# Print up to 3 commit subjects from branch $1 as a comma-separated +# list, escaped for markdown table use. +format_commits() { + $HOME/.guix-home/profile/bin/git -C "$CLAUDE_PROJECT_DIR" log --oneline \ + --format='%s' main.."$1" 2>/dev/null \ + | head -3 \ + | $GUIX_BIN/sed 's/|/\\|/g' \ + | $GUIX_BIN/tr '\n' ',' \ + | $GUIX_BIN/sed 's/,$//;s/,/, /g' +} + +# Table header. +$GUIX_BIN/echo '| Branch | Commits | Summary |' +$GUIX_BIN/echo '|--------|---------|---------|' + +# One row per branch. +echo "$branches" | while read -r b; do + count=$($HOME/.guix-home/profile/bin/git \ + -C "$CLAUDE_PROJECT_DIR" rev-list --count \ + main.."$b" 2>/dev/null) + if [ -z "$count" ] || [ "$count" -eq 0 ]; then + summary="(even with main)" + else + summary=$(format_commits "$b") + fi + $GUIX_BIN/echo "| \`$b\` | $count | $summary |" +done + +exit 0 diff --git a/.claude/scripts/merge-worktree b/.claude/scripts/merge-worktree new file mode 100755 index 0000000..492e45a --- /dev/null +++ b/.claude/scripts/merge-worktree @@ -0,0 +1,82 @@ +#!/bin/sh +# Copyright (c) 2026 Jakub Czajka +# License: GPL-3.0 or later. +# +# merge-worktree — Merge a worktree branch into main. +# +# This script operates on the main worktree (CLAUDE_PROJECT_DIR) as a +# subprocess, independent of the session's current directory. It rebases +# the branch for linear history then fast-forwards main. +# +# Usage: merge-worktree + +set -eu + +branch="${1:-}" +if [ -z "$branch" ]; then + echo "Usage: merge-worktree " >&2 + exit 1 +fi + +PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}" + +# Validate branch. +if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" rev-parse \ + --verify "$branch" >/dev/null 2>&1; then + echo "Error: Branch '$branch' does not exist." >&2 + exit 1 +fi + +if [ "$branch" = "main" ]; then + echo "Error: Cannot merge main into itself." >&2 + exit 1 +fi + +# Check main worktree is clean. +if [ -n "$($HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \ + status --porcelain --untracked-files=no)" ]; then + echo "Error: Main worktree has uncommitted changes." >&2 + echo " Commit or stash them first." >&2 + exit 1 +fi + +# Ensure main is restored on any exit (rebase failure, interruption). +trap '$HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" checkout main \ + 2>/dev/null || true' EXIT + +# Fetch from origin. +echo "=== Fetching from origin ===" +$HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" fetch origin + +# Rebase branch onto main. +echo "=== Rebasing $branch onto main ===" +if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \ + rebase main "$branch"; then + rc=$? + echo "Error: Rebase failed (conflicts)." >&2 + echo " Resolve them in the main worktree:" >&2 + echo " cd $PROJECT_DIR" >&2 + echo " git status # see conflicted files" >&2 + echo " # fix conflicts, then:" >&2 + echo " git rebase --continue" >&2 + echo " git checkout main" >&2 + echo " git merge --ff-only $branch" >&2 + exit $rc +fi + +# Switch back to main. +echo "=== Switching to main ===" +$HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" checkout main + +# Fast-forward merge. +echo "=== Merging $branch into main (fast-forward) ===" +if ! $HOME/.guix-home/profile/bin/git -C "$PROJECT_DIR" \ + merge --ff-only "$branch"; then + rc=$? + echo "Error: Fast-forward merge failed." >&2 + echo " This should not happen after a successful rebase." >&2 + echo " Try: git -C $PROJECT_DIR merge $branch" >&2 + exit $rc +fi + +echo "=== Branch '$branch' successfully merged into main ===" diff --git a/.claude/skills/merge-worktree.md b/.claude/skills/merge-worktree.md new file mode 100644 index 0000000..1a1f6af --- /dev/null +++ b/.claude/skills/merge-worktree.md @@ -0,0 +1,48 @@ +--- +name: merge-worktree +description: Merge a workspace branch into main and optionally reconfigure. +--- + +# merge-worktree + +Merge a worktree branch into main, rebasing for linear history, then +optionally run guix reconfigure. + +## Instructions + +When the user asks to merge a workspace, integrate branch changes, or +"commit to main": + +1. **Show available workspaces**: + Run `.claude/scripts/branch-summary` to display a markdown table of + all non-main branches (workspaces) and their commits. + +2. **Ask which workspace to merge**: + Use AskUserQuestion (single-select). Present the workspace branch + names from step 1 as options. If only one non-main branch exists, + ask to confirm merging that one. + +3. **Execute the merge**: + Run `.claude/scripts/merge-worktree `. + If the merge fails (rebase conflict), instruct the user to resolve + conflicts in the main worktree at `$CLAUDE_PROJECT_DIR`, then retry. + +4. **Ask about guix reconfigure**: + Use AskUserQuestion (single-select) with these options: + - "guix home reconfigure" + - "guix system reconfigure" + - "Skip reconfigure" + +5. **Run reconfigure if selected**: + Run the chosen command using Bash against the main worktree. + +6. **Offer branch cleanup**: + Use AskUserQuestion (single-select) asking whether to delete the + merged branch. Options: "Delete branch and worktree", "Keep branch". + +## Notes + +- The merge script `cd`s to `$CLAUDE_PROJECT_DIR` (main worktree) as a + subprocess, independent of the session's worktree directory. +- After the merge, `ExitWorktree` becomes available since the branch is + now an ancestor of main. -- 2.47.3