/git:upstream-pr
Submit clean, atomic PRs to upstream repositories from fork work.
When to Use This Skill
| Use this skill when... |
Use something else instead when... |
| Contributing changes back to the upstream repo |
Committing to your own fork/repo → /git:commit |
| Cherry-picking fork commits for upstream PR (single or multi-commit, squashing) |
Creating a PR within the same repo → /git:pr |
| Fork and upstream are roughly aligned |
Fork has substantially diverged from upstream → /git:upstream-pr-diverged (patch-id eligibility, re-derive fallback, scrubbing) |
| Need a clean branch based on upstream/main |
Working on a branch already tracking upstream |
Context
- Current branch: !
git branch --show-current
- Git status: !
git status --porcelain=v2 --branch
- Remotes: !
git remote -v
- Has upstream: !
git remote -v
- Origin URL: !
git remote -v
- Upstream URL: !
git remote -v
- Recent commits: !
git log --oneline --max-count=20
- Stash list: !
git stash list
Parameters
Parse these from $ARGUMENTS:
| Parameter |
Required |
Description |
--commits sha1,sha2,... |
No |
Comma-separated commit SHAs to cherry-pick (interactive selection if omitted) |
--branch name |
No |
Name for the upstream PR branch (auto-generated if omitted) |
--upstream owner/repo |
No |
Override upstream repo (detected from remote if omitted) |
--draft |
No |
Create PR as draft |
--dry-run |
No |
Show what would happen without making changes |
Execution
Execute this fork-to-upstream PR workflow:
Step 1: Validate fork environment
- Verify this is a git repository:
git rev-parse --git-dir
- Check if
upstream remote exists (from context)
- If no
upstream remote:
- Try to detect via
gh repo view --json parent -q '.parent.nameWithOwner'
- If found, add it:
git remote add upstream https://github.com/<owner/repo>.git
- If not found, ask user with AskUserQuestion for the upstream repo URL
- Fetch upstream:
git fetch upstream
- Record the current branch name and check for uncommitted changes
- If uncommitted changes exist, stash them:
git stash push -m "upstream-pr: WIP before upstream PR"
Step 2: Assess fork state
Report the fork's divergence from upstream:
Fork status:
Upstream: <upstream-repo>
Behind upstream/main: N commits
Ahead of upstream/main: M commits
If --dry-run is set, also show which commits would be selected and stop here.
Step 3: Select commits
If --commits provided:
- Parse the comma-separated SHA list
- Validate each SHA exists:
git rev-parse --verify <sha>
- Show summary of selected commits
If --commits not provided:
Step 4: Create clean branch from upstream/main
- Generate branch name if
--branch not provided:
- Extract scope from first commit message (e.g.,
feat(auth): add OAuth -> feat/auth-add-oauth)
- Fallback:
upstream-pr/<date>
- Create branch from upstream/main:
git switch -c <branch-name> upstream/main
Step 5: Cherry-pick and squash
- Cherry-pick each selected commit in order:
git cherry-pick <sha1> <sha2> ...
- If cherry-pick conflicts occur:
- Report the conflict to the user
- Reference git-conflicts for resolution
- After resolution:
git cherry-pick --continue
- Squash all cherry-picked commits into one atomic commit:
git reset --soft upstream/main
git commit -m "<conventional commit message>"
- Use AskUserQuestion to confirm or edit the commit message
- Default message: derived from the cherry-picked commits
- Format: conventional commit with scope
Step 6: Push to fork
git push -u origin <branch-name>
If --dry-run, show the command without executing.
Step 7: Create cross-fork PR
- Determine the upstream repo (from
--upstream or detected remote)
- Determine the fork owner (from
origin remote URL)
- Create the PR:
gh pr create \
--repo <upstream-owner/upstream-repo> \
--base main \
--head <fork-owner>:<branch-name> \
--title "<conventional commit title>" \
--body "<PR description>"
Add --draft if the flag was provided.
- Report the PR URL to the user
Step 8: Restore working state
- Switch back to the original branch:
git switch <original-branch>
- If changes were stashed in Step 1, pop them:
git stash pop
- Report completion summary:
Upstream PR created:
PR: <url>
Branch: <branch-name>
Commits: N cherry-picked, squashed into 1
Original branch restored: <branch>
Error Handling
| Error |
Recovery |
| Cherry-pick conflict |
Show conflicted files, reference git-conflicts skill |
| Push rejected |
Check if branch already exists on fork; suggest --branch override |
| No upstream remote |
Auto-detect from gh repo view --json parent or ask user |
| Upstream/main not found |
Try upstream/master; ask user for correct branch name |
| No commits ahead of upstream |
Report "Nothing to contribute" and exit |
Agentic Optimizations
| Context |
Command |
| Divergence check |
git rev-list --left-right --count upstream/main...HEAD |
| Commits ahead |
git log --oneline --format='%h %s' upstream/main..HEAD |
| Validate SHA |
git rev-parse --verify --short <sha> |
| Fork owner |
git remote get-url origin | sed -E 's#.*github.com[:/]##; s#\.git$##; s#/.*##' |
| Upstream repo |
git remote get-url upstream | sed -E 's#.*github.com[:/]##; s#\.git$##' |
| Cross-fork PR |
gh pr create --repo <upstream> --head <fork-owner>:<branch> |
Related Skills
1---
2name: git-upstream-pr
3description: "Submit PRs to upstream repos from a fork — commit selection, squashing, cross-fork creation. Use when contributing changes upstream or cherry-picking fork commits."
4args: "[--commits sha1,sha2] [--branch name] [--upstream owner/repo] [--draft] [--dry-run]"
5allowed-tools: Bash(git status *), Bash(git diff *), Bash(git log *), Bash(git remote *), Bash(git fetch *), Bash(git switch *), Bash(git cherry-pick *), Bash(git reset *), Bash(git commit *), Bash(git push *), Bash(git stash *), Bash(git rev-list *), Bash(git rev-parse *), Bash(git branch *), Bash(gh pr *), Bash(gh repo *), Bash(gh auth *), AskUserQuestion, Read, Grep, Glob, TodoWrite
6argument-hint: --commits abc123,def456 or --branch feat/my-upstream-pr
7disable-model-invocation: true
8created: 2026-03-02
9modified: 2026-06-18
10reviewed: 2026-03-02
11---
12
13# /git:upstream-pr
14
15Submit clean, atomic PRs to upstream repositories from fork work.
16
17## When to Use This Skill
18
19| Use this skill when... | Use something else instead when... |
20|------------------------|------------------------------------|
21| Contributing changes back to the upstream repo | Committing to your own fork/repo → `/git:commit` |
22| Cherry-picking fork commits for upstream PR (single or multi-commit, squashing) | Creating a PR within the same repo → `/git:pr` |
23| Fork and upstream are roughly aligned | Fork has substantially diverged from upstream → `/git:upstream-pr-diverged` (patch-id eligibility, re-derive fallback, scrubbing) |
24| Need a clean branch based on upstream/main | Working on a branch already tracking upstream |
25
26## Context
27
28- Current branch: !`git branch --show-current`
29- Git status: !`git status --porcelain=v2 --branch`
30- Remotes: !`git remote -v`
31- Has upstream: !`git remote -v`
32- Origin URL: !`git remote -v`
33- Upstream URL: !`git remote -v`
34- Recent commits: !`git log --oneline --max-count=20`
35- Stash list: !`git stash list`
36
37## Parameters
38
39Parse these from `$ARGUMENTS`:
40
41| Parameter | Required | Description |
42|-----------|----------|-------------|
43| `--commits sha1,sha2,...` | No | Comma-separated commit SHAs to cherry-pick (interactive selection if omitted) |
44| `--branch name` | No | Name for the upstream PR branch (auto-generated if omitted) |
45| `--upstream owner/repo` | No | Override upstream repo (detected from remote if omitted) |
46| `--draft` | No | Create PR as draft |
47| `--dry-run` | No | Show what would happen without making changes |
48
49## Execution
50
51Execute this fork-to-upstream PR workflow:
52
53### Step 1: Validate fork environment
54
551. Verify this is a git repository: `git rev-parse --git-dir`
562. Check if `upstream` remote exists (from context)
573. If no `upstream` remote:
58 - Try to detect via `gh repo view --json parent -q '.parent.nameWithOwner'`
59 - If found, add it: `git remote add upstream https://github.com/<owner/repo>.git`
60 - If not found, ask user with AskUserQuestion for the upstream repo URL
614. Fetch upstream: `git fetch upstream`
625. Record the current branch name and check for uncommitted changes
636. If uncommitted changes exist, stash them: `git stash push -m "upstream-pr: WIP before upstream PR"`
64
65### Step 2: Assess fork state
66
67Report the fork's divergence from upstream:
68
69```
70Fork status:
71 Upstream: <upstream-repo>
72 Behind upstream/main: N commits
73 Ahead of upstream/main: M commits
74```
75
76If `--dry-run` is set, also show which commits would be selected and stop here.
77
78### Step 3: Select commits
79
80**If `--commits` provided:**
81- Parse the comma-separated SHA list
82- Validate each SHA exists: `git rev-parse --verify <sha>`
83- Show summary of selected commits
84
85**If `--commits` not provided:**
86- Show recent commits that are ahead of upstream/main:
87 ```bash
88 git log --oneline --format='%h %s (%cr)' upstream/main..HEAD
89 ```
90- Use AskUserQuestion to let the user select which commits to include
91- Present commits as options with their subject lines
92
93### Step 4: Create clean branch from upstream/main
94
951. Generate branch name if `--branch` not provided:
96 - Extract scope from first commit message (e.g., `feat(auth): add OAuth` -> `feat/auth-add-oauth`)
97 - Fallback: `upstream-pr/<date>`
982. Create branch from upstream/main:
99 ```bash
100 git switch -c <branch-name> upstream/main
101 ```
102
103### Step 5: Cherry-pick and squash
104
1051. Cherry-pick each selected commit in order:
106 ```bash
107 git cherry-pick <sha1> <sha2> ...
108 ```
1092. If cherry-pick conflicts occur:
110 - Report the conflict to the user
111 - Reference [git-conflicts](../git-conflicts/SKILL.md) for resolution
112 - After resolution: `git cherry-pick --continue`
1133. Squash all cherry-picked commits into one atomic commit:
114 ```bash
115 git reset --soft upstream/main
116 git commit -m "<conventional commit message>"
117 ```
1184. Use AskUserQuestion to confirm or edit the commit message
119 - Default message: derived from the cherry-picked commits
120 - Format: conventional commit with scope
121
122### Step 6: Push to fork
123
124```bash
125git push -u origin <branch-name>
126```
127
128If `--dry-run`, show the command without executing.
129
130### Step 7: Create cross-fork PR
131
1321. Determine the upstream repo (from `--upstream` or detected remote)
1332. Determine the fork owner (from `origin` remote URL)
1343. Create the PR:
135 ```bash
136 gh pr create \
137 --repo <upstream-owner/upstream-repo> \
138 --base main \
139 --head <fork-owner>:<branch-name> \
140 --title "<conventional commit title>" \
141 --body "<PR description>"
142 ```
143 Add `--draft` if the flag was provided.
1444. Report the PR URL to the user
145
146### Step 8: Restore working state
147
1481. Switch back to the original branch:
149 ```bash
150 git switch <original-branch>
151 ```
1522. If changes were stashed in Step 1, pop them:
153 ```bash
154 git stash pop
155 ```
1563. Report completion summary:
157 ```
158 Upstream PR created:
159 PR: <url>
160 Branch: <branch-name>
161 Commits: N cherry-picked, squashed into 1
162 Original branch restored: <branch>
163 ```
164
165## Error Handling
166
167| Error | Recovery |
168|-------|----------|
169| Cherry-pick conflict | Show conflicted files, reference git-conflicts skill |
170| Push rejected | Check if branch already exists on fork; suggest `--branch` override |
171| No upstream remote | Auto-detect from `gh repo view --json parent` or ask user |
172| Upstream/main not found | Try `upstream/master`; ask user for correct branch name |
173| No commits ahead of upstream | Report "Nothing to contribute" and exit |
174
175## Agentic Optimizations
176
177| Context | Command |
178|---------|---------|
179| Divergence check | `git rev-list --left-right --count upstream/main...HEAD` |
180| Commits ahead | `git log --oneline --format='%h %s' upstream/main..HEAD` |
181| Validate SHA | `git rev-parse --verify --short <sha>` |
182| Fork owner | `git remote get-url origin \| sed -E 's#.*github.com[:/]##; s#\.git$##; s#/.*##'` |
183| Upstream repo | `git remote get-url upstream \| sed -E 's#.*github.com[:/]##; s#\.git$##'` |
184| Cross-fork PR | `gh pr create --repo <upstream> --head <fork-owner>:<branch>` |
185
186## Related Skills
187
188- [git-fork-workflow](../git-fork-workflow/SKILL.md) - Fork management and sync strategies
189- [git-branch-pr-workflow](../git-branch-pr-workflow/SKILL.md) - General branch and PR patterns
190- [git-conflicts](../git-conflicts/SKILL.md) - Resolve cherry-pick conflicts
191- [git-rebase-patterns](../git-rebase-patterns/SKILL.md) - Advanced rebase techniques
192- [gh-cli-agentic](../gh-cli-agentic/SKILL.md) - GitHub CLI cross-fork PR fields