Upstream PR Workflow
This skill manages the full lifecycle of contributing a fix from a fork back to an
upstream repository, following a draft-in-tree convention: a PULL_REQUEST_DRAFT.md
file lives in every commit on the PR branch and is git rm'd immediately before
submission.
The PULL_REQUEST_DRAFT.md Convention
Every commit on a PR branch must contain PULL_REQUEST_DRAFT.md at the repo root.
Format:
# PR Title Here (this line → `gh pr create --title`)
Everything below this line becomes the PR body (`--body`).
## Problem
…
## Solution
…
## Testing
…
- The first
# H1line is the PR title. - All remaining content is the PR body (GitHub Markdown).
- The file is committed so every intermediate state of the branch is self-documenting.
- Do NOT
.gitignoreit — it must be visible ingit logand reviewable. - Remove it with
git rmjust before creating the PR (see Submission step).
Remotes Convention
In a fork, always name remotes:
| Remote | Points to |
|---|---|
origin |
Your fork (read/write) |
upstream |
The original project |
If upstream is not configured:
git remote add upstream https://github.com/OWNER/REPO.git
git fetch upstream
The akaihola Working Branch (git-knit)
All unmerged feature branches must be stacked into a single working branch
called akaihola using git-knit. This gives a single integration branch that
combines every in-flight change on top of upstream/main.
Initialize (once per repo)
# akaihola = working branch, upstream/main = base, list any existing branches
git knit init akaihola upstream/main [fix/branch-a fix/branch-b ...]
If akaihola already exists in the remote, verify git knit status matches
what is currently checked out before touching it.
Day-to-day commands
git knit status # show base, working branch, and all knitted branches
git knit add fix/new-branch # add a newly-created feature branch
git knit remove fix/old-branch # drop a merged/abandoned branch
git knit rebuild # rebuild akaihola from scratch (after rebases, etc.)
git knit restack # restack feature branches with git-spice
Rules
- Every new feature/fix branch must be
git knit add-ed immediately after creation. - After rebasing any feature branch onto updated
upstream/main, rungit knit rebuildto regenerateakaihola. - Never commit directly to
akaihola— it is a derived branch. All real commits go to individualfix/*/feat/*/docs/*branches. - When a PR is merged upstream, run
git knit remove <branch>andgit knit rebuild.
Workflow
1. Identify the change
Determine what needs contributing. Common sources:
- A commit in your fork's
mainnot inupstream/main - A bug fix applied locally but never submitted
- An enhancement developed in a feature branch
Useful commands:
# Commits in fork's main not in upstream
git log --oneline upstream/main..main
# Diff a specific file between fork and upstream
git diff upstream/main..main -- path/to/file.go
# Check if content already merged upstream (empty = already there)
git cherry -v upstream/main <your-branch>
Always verify the change is not already in upstream before creating a PR branch.
Upstream may have merged the content under different commit SHAs. Use
git diff upstream/main...<your-branch> (three dots = content diff from merge base)
to confirm actual differences exist.
2. Fetch upstream
git fetch upstream
3. Create a clean branch from upstream/main
git checkout -b <branch-name> upstream/main
Branch naming convention:
fix/<short-description>for bug fixesfeat/<short-description>for new featureschore/<short-description>for non-functional changesdocs/<short-description>for documentation
4. Apply the change
Choose the cleanest approach:
a) Cherry-pick (when the commit applies cleanly):
git cherry-pick <commit-sha>
# Resolve any conflicts if needed
b) Apply a file diff (when cherry-pick would drag in unrelated changes):
git diff upstream/main..main -- path/to/file > /tmp/fix.patch
git apply /tmp/fix.patch
git add path/to/file
git commit -m "fix: description of the fix"
c) Implement fresh (when rewriting is cleaner than patching): Just make the changes and commit normally.
5. Add PULL_REQUEST_DRAFT.md to the commit
After the fix commit exists (or as part of it), add PULL_REQUEST_DRAFT.md:
cat > PULL_REQUEST_DRAFT.md << 'EOF'
# fix: concise imperative title matching upstream's commit style
## Problem
Describe what was broken and why it mattered.
## Solution
Explain what the fix does. Reference the functions/files changed.
## Testing
How to verify the fix works. Include commands if applicable.
EOF
git add PULL_REQUEST_DRAFT.md
git commit --amend --no-edit # or: git commit -m "..." if draft is a separate commit
For multi-commit PRs, include PULL_REQUEST_DRAFT.md in every commit
(use git commit --amend or update it at each step). The file should always
reflect the PR's current intended title and description.
6. Push the branch to origin
git push origin <branch-name>
Note: the agent cannot push to GitHub. Remind the user to run ghpp on the desktop machine
or use git push from a machine with push access.
7. Submit the PR (submission step)
When the branch is ready to submit, use the helper script:
# From the repo root, with the PR branch checked out:
bash /path/to/skills-akaihola/upstream-pr/scripts/submit-pr.sh [--upstream-repo OWNER/REPO]
The script:
- Reads
PULL_REQUEST_DRAFT.mdand extracts title + body - Shows a preview and asks for confirmation
- Runs
git rm PULL_REQUEST_DRAFT.md && git commit -m "chore: remove PR draft before submission" - Runs
git push origin <branch>(if needed) - Runs
gh pr create --repo OWNER/REPO --title "..." --body "..."
Manual equivalent (if not using the script):
TITLE=$(grep -m1 '^# ' PULL_REQUEST_DRAFT.md | sed 's/^# //')
BODY=$(tail -n +2 PULL_REQUEST_DRAFT.md | sed '1{/^$/d}')
git rm PULL_REQUEST_DRAFT.md
git commit -m "chore: remove PR draft before submission"
git push origin <branch>
gh pr create --repo OWNER/REPO --title "$TITLE" --body "$BODY"
Handling Conflicts
When cherry-picking produces conflicts:
- Inspect the conflict with
git diffand understand both sides. - Resolve by keeping the most correct version — usually upstream's recent refactors plus your new logic on top.
git addresolved files, thengit cherry-pick --continue.- Do not use
--strategy-option theirs/oursblindly — read the conflict.
Verifying the Branch is Clean
Before pushing, confirm the branch only contains what you intend:
# Should list only your PR commits
git log --oneline upstream/main..<branch>
# Should show only your intended file changes
git diff --stat upstream/main...<branch>
# PULL_REQUEST_DRAFT.md must be present
git show HEAD:PULL_REQUEST_DRAFT.md | head -3
Keeping Branches Updated
If upstream/main advances while your PR is under review:
git fetch upstream
git rebase upstream/main
# Update PULL_REQUEST_DRAFT.md if anything changed
git push --force-with-lease origin <branch>
Example: Full Single-Fix PR
cd ~/mitto
git fetch upstream
git checkout -b fix/my-bug-fix upstream/main
git cherry-pick abc1234
cat > PULL_REQUEST_DRAFT.md << 'EOF'
# fix(web): correct the thing that was broken
## Problem
Widget exploded when user did X.
## Solution
Guard against nil pointer in `handleFoo()`.
## Testing
`go test ./internal/web/...` passes. Manually verified: X no longer explodes.
EOF
git add PULL_REQUEST_DRAFT.md
git commit --amend --no-edit
git push origin fix/my-bug-fix
# Then ask the user to push with `ghpp` on the desktop machine, or run the submit script
Notes
- Always target
upstream/mainas the base, notorigin/mainormain. - Always verify the fix is not already upstream before creating a branch.
- If the upstream repo uses squash-merge, a single commit per PR is cleaner.
- Keep PR branches focused: one logical change per branch.
- Close the corresponding upstream issue in the PR body with
Fixes #N.