resolving-merge-conflicts

Installation
SKILL.md
  1. Orient before you touch anything. Establish from git status whether you are in a merge, a rebase, a cherry-pick, a revert, a git am, or a stash pop. Unmerged paths with none of the other five in progress mean a conflicting stash pop; confirm it with git stash list and the request, and ask the user when their origin is unclear. When none is in progress and nothing is unmerged, start a merge only from a clean worktree. When git status --porcelain lists anything, stop and ask the user to commit it, stash it, or move it to a separate worktree; never stash it yourself. For a PR, the base repository is the PR URL's <owner>/<repo>; read the base ref, its tip, the head branch, and the head repository with gh pr view <url> --json baseRefName,baseRefOid,headRefName,headRefOid,headRepository. The base branch is the one the request names, else the PR's baseRefName, else the default branch from gh repo view --json defaultBranchRef. When the head branch, headRefName for a PR, is not checked out, stop and ask. Before starting a merge or rebase for a PR, fetch refs/pull/<number>/head from the remote that resolves to the base repository, which works for fork PRs too, and require both the fetched ref and local HEAD to equal headRefOid. On a mismatch, stop, show headRefOid and every SHA that differs from it, and ask, because the merge has to start from what the PR holds. Fetch from the remote that resolves to the base repository, require the fetched base ref to equal baseRefOid when the base is the PR's, merge that remote's base ref into the head branch, or rebase onto it when the user asked for a rebase, and orient. Rebase only when the user asked for one, because rebasing a published branch rewrites its history; step 5's preflight-mutations gate covers its push. Under a rebase ours is the upstream you are replaying onto and theirs is your own commit, the inverse of a merge, and getting this backwards is the most common wrong resolution.

    Then split the conflicted files. Regenerate generated artifacts and lockfiles from source, never hand-merge them. That means bun.lock, pnpm-lock.yaml, _generated/, and snapshots. Only hand-written files go through steps 2-3. Under a rebase, stop and ask before touching a conflicted Drizzle migration file, because deleting the head's SQL there rewrites the commit that added it. Under a merge, a conflicted Drizzle meta/_journal.json or meta/<idx>_snapshot.json takes the base side. Delete the head's own migration SQL in the same merge, or take the base side when the base added the same path, and record the head's SQL as dropped in the ledger either way. When refresh-drizzle-migration did not start this merge, first run its step 1 config discovery, head-migration listing, and base-owned check with MERGE_HEAD as <base-ref>, before resolving any Drizzle file, and stop on any path it flags, because taking the base side there discards the head's edit to a migration the base already has. When this merge drops the head's migration, skip step 4, since checks run against a schema with no migration fail until it is regenerated and refresh-drizzle-migration runs done after regeneration, or on the merge when the refresh stops before that run. Finish at step 5. When refresh-drizzle-migration did not start this merge, run its step 1 head-migration listing against the remote-tracking branch (<remote>/<branch>) this merge brought in, which must contain HEAD^2. When that listing finds a head migration in any config, including one this merge or an earlier one dropped, invoke refresh-drizzle-migration before any push with that branch as its base, so the branch never ships a schema change without its migration.

  2. Find the primary sources for each conflict. Learn why each side made its change. Read the commit messages, check the PRs, check original issues and tickets.

  3. Resolve each hunk against the primary sources, not against the markers. Preserve both intents. Where incompatible, pick the one matching the merge's stated goal and note the trade-off. Every line in the resolution traces to one side's original or to a mechanical combination of both.

    Before staging, diff your resolution against both parents for every conflicted file. Run git diff --cc <file>, or git diff :2:<file> <file> and git diff :3:<file> <file>. Place every hunk from both sides in exactly one bucket. The buckets are kept, superseded with a note naming what replaced it, or dropped with a note naming why. A hunk you cannot place is a silent drop, an unresolved conflict wearing a resolution. State the ledger before you commit.

    Never abort the merge to dodge a hard hunk. When the two intents are genuinely incompatible and no stated goal decides between them, stop and put both candidate resolutions to the user rather than guessing.

  4. Run the project's checks. /done owns this pipeline. Let it select the acceptance lanes, verify each one, assign states, and build its readiness card. Stop at that card and take none of the handoffs its final section ends in. Committing mid-merge concludes the merge with a generated conventional subject instead of the merge message, so git-commit and file-pr both wait for step 5. Fix anything the merge broke.

  5. Finish the merge or rebase. When the operation rewrites commits or refs already published or consumed, invoke preflight-mutations immediately before continuing that published-history operation or updating its shared ref. Pass the exact operation, local, upstream, base, and head SHAs, affected remote refs, dependent branches and PRs, recovery ref, and explicit rewrite authorization. Apply its result contract before continuing. Unpublished local conflict resolution does not use this gate.

    Stage everything and commit. If rebasing, continue until all commits are replayed. The same conflict often resurfaces at each replayed commit, and your resolution must stay consistent across them.

Installs
132
GitHub Stars
2
First Seen
Jul 23, 2026