mirror of
https://github.com/pnpm/pnpm.git
synced 2026-03-29 04:21:39 -04:00
docs: add PR conflict resolution guide and helper script (#11060)
Add a "Resolving Conflicts in GitHub PRs" section to AGENTS.md with step-by-step instructions for force-fetching refs, rebasing, resolving lockfile conflicts, and verifying mergeability. Add shell/resolve-pr-conflicts.sh that automates the workflow: fetches metadata, force-updates the base ref, rebases, auto-resolves lockfile conflicts via pnpm install, force-pushes, and verifies the result.
This commit is contained in:
10
AGENTS.md
10
AGENTS.md
@@ -187,6 +187,16 @@ try {
|
||||
}
|
||||
```
|
||||
|
||||
## Resolving Conflicts in GitHub PRs
|
||||
|
||||
Use `shell/resolve-pr-conflicts.sh` to resolve PR conflicts:
|
||||
|
||||
```bash
|
||||
./shell/resolve-pr-conflicts.sh <PR_NUMBER>
|
||||
```
|
||||
|
||||
The script force-fetches the base branch (avoiding stale refs), rebases, auto-resolves `pnpm-lock.yaml` conflicts via `pnpm install`, force-pushes, and verifies GitHub sees the PR as mergeable. For non-lockfile conflicts it will pause and list the files that need manual resolution.
|
||||
|
||||
## Key Configuration Files
|
||||
|
||||
- `pnpm-workspace.yaml`: Defines the workspace structure.
|
||||
|
||||
184
shell/resolve-pr-conflicts.sh
Executable file
184
shell/resolve-pr-conflicts.sh
Executable file
@@ -0,0 +1,184 @@
|
||||
#!/usr/bin/env bash
|
||||
# Resolves merge conflicts for a GitHub PR by rebasing onto the latest base branch.
|
||||
#
|
||||
# Usage:
|
||||
# ./shell/resolve-pr-conflicts.sh <PR_NUMBER> # full run
|
||||
# ./shell/resolve-pr-conflicts.sh <PR_NUMBER> --continue # finish after manual resolution
|
||||
#
|
||||
# Prerequisites:
|
||||
# - gh CLI authenticated with access to pnpm/pnpm
|
||||
# - "origin" remote must point to pnpm/pnpm (not a fork)
|
||||
# - You must be on the PR's head branch (the script will checkout via gh if not)
|
||||
#
|
||||
# This script:
|
||||
# 1. Checks out the PR branch if needed
|
||||
# 2. Force-fetches the base branch to avoid stale refs
|
||||
# 3. Rebases the current branch onto it
|
||||
# 4. Auto-resolves pnpm-lock.yaml conflicts via lockfile-only install
|
||||
# 5. For other conflicts, exits with the list of files needing manual resolution
|
||||
# 6. After manual resolution, call with --continue to finish (rebase continue + push + verify)
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
PR_NUMBER="${1:?Usage: $0 <PR_NUMBER> [--continue]}"
|
||||
CONTINUE_MODE="${2:-}"
|
||||
REPO="pnpm/pnpm"
|
||||
|
||||
# Verify origin points to pnpm/pnpm (strict match for HTTPS or SSH)
|
||||
ORIGIN_URL=$(git remote get-url origin 2>/dev/null || echo "")
|
||||
if [[ ! "$ORIGIN_URL" =~ github\.com[:/]pnpm/pnpm(\.git)?$ ]]; then
|
||||
echo "ERROR: 'origin' remote does not point to pnpm/pnpm."
|
||||
echo " Current origin: $ORIGIN_URL"
|
||||
echo " Expected: https://github.com/pnpm/pnpm.git (or git@github.com:pnpm/pnpm.git)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get PR metadata
|
||||
echo "Fetching PR #${PR_NUMBER} metadata..."
|
||||
HEAD_OWNER=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json headRepositoryOwner --jq .headRepositoryOwner.login)
|
||||
HEAD_BRANCH=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json headRefName --jq .headRefName)
|
||||
|
||||
# Ensure we're on the PR branch (do this before determining push remote so gh can set up fork remotes)
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")
|
||||
if [ "$CURRENT_BRANCH" != "$HEAD_BRANCH" ]; then
|
||||
echo "Not on PR branch ($CURRENT_BRANCH != $HEAD_BRANCH). Checking out via gh..."
|
||||
gh pr checkout "$PR_NUMBER"
|
||||
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
if [ "$CURRENT_BRANCH" != "$HEAD_BRANCH" ]; then
|
||||
echo "ERROR: Failed to checkout PR branch. Current branch: $CURRENT_BRANCH"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Determine push remote (after checkout, since gh pr checkout may add the fork remote)
|
||||
REMOTE="origin"
|
||||
if [ "$HEAD_OWNER" != "pnpm" ]; then
|
||||
if git remote get-url "$HEAD_OWNER" &>/dev/null; then
|
||||
REMOTE="$HEAD_OWNER"
|
||||
else
|
||||
# Try to auto-add the fork remote from the PR's clone URL
|
||||
FORK_URL="https://github.com/$HEAD_OWNER/pnpm.git"
|
||||
echo "Adding remote '$HEAD_OWNER' -> $FORK_URL"
|
||||
git remote add "$HEAD_OWNER" "$FORK_URL"
|
||||
REMOTE="$HEAD_OWNER"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Helper: regenerate lockfile without running lifecycle scripts
|
||||
regenerate_lockfile() {
|
||||
echo "Regenerating pnpm-lock.yaml..."
|
||||
pnpm install --lockfile-only --no-frozen-lockfile --ignore-scripts
|
||||
git add pnpm-lock.yaml
|
||||
}
|
||||
|
||||
# --continue mode: finish a previously paused rebase, then push
|
||||
if [ "$CONTINUE_MODE" = "--continue" ]; then
|
||||
echo "Continuing rebase..."
|
||||
|
||||
# Regenerate lockfile if it was among the conflicted files
|
||||
if git diff --name-only --diff-filter=U 2>/dev/null | grep -q "pnpm-lock.yaml"; then
|
||||
echo "Auto-resolving pnpm-lock.yaml..."
|
||||
git checkout --ours pnpm-lock.yaml
|
||||
git add pnpm-lock.yaml
|
||||
regenerate_lockfile
|
||||
fi
|
||||
|
||||
if ! GIT_EDITOR=true git rebase --continue; then
|
||||
echo "ERROR: 'git rebase --continue' failed. Resolve remaining conflicts and re-run with --continue."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Force-pushing to $REMOTE/$HEAD_BRANCH..."
|
||||
git push "$REMOTE" "HEAD:$HEAD_BRANCH" --force-with-lease
|
||||
|
||||
echo "Waiting for GitHub to update mergeability..."
|
||||
sleep 10
|
||||
MERGEABLE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json mergeable --jq .mergeable)
|
||||
echo "PR mergeable: $MERGEABLE"
|
||||
[ "$MERGEABLE" = "MERGEABLE" ] && echo "Conflicts resolved successfully!" || echo "WARNING: GitHub still reports conflicts. Re-run this script."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Full mode: fetch, rebase, resolve
|
||||
|
||||
BASE_BRANCH=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json baseRefName --jq .baseRefName)
|
||||
echo "Base: $BASE_BRANCH Head: $HEAD_OWNER:$HEAD_BRANCH"
|
||||
|
||||
# Force-update the base branch ref (use + prefix to force non-fast-forward updates)
|
||||
echo "Force-fetching origin/$BASE_BRANCH..."
|
||||
git fetch origin "+refs/heads/$BASE_BRANCH:refs/remotes/origin/$BASE_BRANCH"
|
||||
|
||||
# Verify against GitHub
|
||||
GITHUB_SHA=$(gh api "repos/$REPO/branches/$BASE_BRANCH" --jq '.commit.sha')
|
||||
LOCAL_SHA=$(git rev-parse "origin/$BASE_BRANCH")
|
||||
if [ "$GITHUB_SHA" != "$LOCAL_SHA" ]; then
|
||||
echo "ERROR: Local origin/$BASE_BRANCH ($LOCAL_SHA) doesn't match GitHub ($GITHUB_SHA)"
|
||||
exit 1
|
||||
fi
|
||||
echo "Base branch ref verified: $LOCAL_SHA"
|
||||
|
||||
# Rebase
|
||||
echo "Rebasing onto origin/$BASE_BRANCH..."
|
||||
if git rebase "origin/$BASE_BRANCH"; then
|
||||
echo "Rebase completed cleanly."
|
||||
else
|
||||
echo "Conflicts detected. Attempting auto-resolution..."
|
||||
|
||||
CONFLICTED=$(git diff --name-only --diff-filter=U)
|
||||
MANUAL_FILES=()
|
||||
|
||||
for file in $CONFLICTED; do
|
||||
if [ "$file" = "pnpm-lock.yaml" ]; then
|
||||
echo " Auto-resolving pnpm-lock.yaml (will regenerate)..."
|
||||
git checkout --ours pnpm-lock.yaml
|
||||
git add pnpm-lock.yaml
|
||||
else
|
||||
MANUAL_FILES+=("$file")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#MANUAL_FILES[@]} -gt 0 ]; then
|
||||
# Regenerate lockfile now if it was conflicted, before pausing
|
||||
if echo "$CONFLICTED" | grep -q "pnpm-lock.yaml"; then
|
||||
regenerate_lockfile
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "MANUAL_RESOLUTION_NEEDED"
|
||||
echo "The following files have conflicts that need manual resolution:"
|
||||
for f in "${MANUAL_FILES[@]}"; do
|
||||
echo " $f"
|
||||
done
|
||||
echo ""
|
||||
echo "After resolving, stage the files with 'git add' and run:"
|
||||
echo " $0 $PR_NUMBER --continue"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# All conflicts were auto-resolved — regenerate lockfile and continue
|
||||
if echo "$CONFLICTED" | grep -q "pnpm-lock.yaml"; then
|
||||
regenerate_lockfile
|
||||
fi
|
||||
|
||||
if ! GIT_EDITOR=true git rebase --continue; then
|
||||
echo "ERROR: 'git rebase --continue' failed. Resolve remaining conflicts and run: $0 $PR_NUMBER --continue"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Force push
|
||||
echo "Force-pushing to $REMOTE/$HEAD_BRANCH..."
|
||||
git push "$REMOTE" "HEAD:$HEAD_BRANCH" --force-with-lease
|
||||
|
||||
# Verify
|
||||
echo "Waiting for GitHub to update mergeability..."
|
||||
sleep 10
|
||||
MERGEABLE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json mergeable --jq .mergeable)
|
||||
MERGE_STATE=$(gh pr view "$PR_NUMBER" --repo "$REPO" --json mergeStateStatus --jq .mergeStateStatus)
|
||||
echo "PR status: mergeable=$MERGEABLE mergeStateStatus=$MERGE_STATE"
|
||||
|
||||
if [ "$MERGEABLE" = "MERGEABLE" ]; then
|
||||
echo "Conflicts resolved successfully!"
|
||||
else
|
||||
echo "WARNING: GitHub still reports conflicts. Main may have moved again — re-run this script."
|
||||
fi
|
||||
@@ -17,7 +17,7 @@ function wt
|
||||
claude --dangerously-skip-permissions "Review and fix PR #$pr_number. Steps:
|
||||
1. Use gh to read the PR description, diff, and all review comments (both PR-level and inline).
|
||||
2. Understand the intent of the PR and what each change does.
|
||||
3. Resolve any conflicts with the base branch: use 'gh pr view $pr_number --json baseRefName' to get the base branch name, then run 'git fetch origin <base> && git merge origin/<base>'. If there are merge conflicts, resolve them. Do NOT skip this step. Do NOT assume the branch is up to date — always fetch and merge to be sure.
|
||||
3. Resolve any conflicts with the base branch by running './shell/resolve-pr-conflicts.sh $pr_number'. This force-fetches the base branch (avoiding stale refs), rebases, and auto-resolves lockfile conflicts. If it prints MANUAL_RESOLUTION_NEEDED, read the listed conflicted files, resolve the conflict markers, run 'git add' on each resolved file, then run './shell/resolve-pr-conflicts.sh $pr_number --continue' to finish the rebase and push. Do NOT skip this step. Do NOT assume the branch is up to date.
|
||||
4. Address every review comment — fix the code as requested or as appropriate.
|
||||
5. Look for any other bugs, issues, or style problems in the changed code and fix those too.
|
||||
6. Run the relevant tests to verify your fixes work (check CLAUDE.md for how to run tests).
|
||||
|
||||
Reference in New Issue
Block a user