Forking and Contributing — The PR That Was Open for 18 Months
A good first PR sat open for 18 months because the contributor forked from the wrong branch.
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
- ✓Git 2.30+, GitHub account, basic command line familiarity, understanding of version control concepts (commit, branch, merge), Node.js 18+ (for running example CI scripts), a text editor or IDE
1. Fork the repo on GitHub. 2. Clone your fork: git clone . 3. Add upstream remote: git remote add upstream . 4. Create a branch: git switch -c feature. 5. Make changes and push. 6. Open a PR from your fork to the original repo.
Imagine you want to suggest a change to a Wikipedia article. You can't edit it directly — you make a copy (fork), edit your copy, then submit a request to merge your changes back (Pull Request). The fork is your private sandbox where you can try anything without affecting the original. The maintainers review your changes and decide whether to accept them.
Forking is the foundation of open-source collaboration. You fork a repo, create changes in your personal copy, and submit a Pull Request. The maintainers review, discuss, and merge. The critical pattern that most beginners miss: keeping your fork in sync with the original (upstream) repository. A PR based on a stale fork will have merge conflicts that grow over time. This guide covers the full fork-and-PR workflow, the incident where a stale fork caused an 18-month-old PR, and how to keep your fork synchronized.
Why Fork? The Case for Controlled Collaboration
Forking a repository creates a personal copy under your GitHub account, giving you full control to experiment without affecting the original project. This is the foundation of the open source contribution model. Unlike direct branching on the upstream repo, forking decouples your work from the project's main development line. This is critical when you're not a core maintainer—you can't push branches to someone else's repo. Forking also protects the upstream from accidental force pushes or broken commits. In production, we've seen teams lose days of work because a contributor force-pushed to a shared branch. Forking eliminates that risk. Always fork before you start coding, even for small fixes. It's a habit that scales.
Syncing Your Fork: Stay Current or Get Left Behind
Your fork is a snapshot. While you work, the upstream repo evolves with new commits, bug fixes, and features. If you don't sync, your pull request will face merge conflicts or become outdated. The standard workflow is to fetch upstream changes and rebase your feature branch on top. Rebasing keeps a linear history, which maintainers prefer. Avoid merging upstream into your branch—it creates unnecessary merge commits. In production, we've seen PRs with 50+ merge commits that could have been a clean 3-commit series. Sync daily, or at least before you open a PR. Use git pull --rebase upstream main to keep your branch current.
--force-with-lease instead of --force. It prevents overwriting remote commits you haven't seen, avoiding accidental data loss.--force-with-lease and wiped out a colleague's commits on a shared branch. Always use the safer flag.Branching Strategy: One Change, One Branch
Each contribution should live on its own branch. This keeps your work isolated and makes it easy to manage multiple PRs simultaneously. Name your branches descriptively: fix/login-error, feat/user-avatar, docs/api-guide. Avoid generic names like patch-1 or my-changes. In production, we enforce a branch naming convention: <type>/<short-description>. This helps CI scripts and maintainers quickly understand the purpose. Never commit directly to main on your fork—that branch should mirror upstream exactly. If you need to update your fork's main, sync it with upstream, don't push to it.
fix/, feat/, chore/, docs/. This is standard in many open source projects and helps with automated changelog generation.Commit Hygiene: Crafting a Clear History
Your commits tell a story. Each commit should be a self-contained, logical unit with a clear message. Follow the Conventional Commits specification: type(scope): description. For example, fix(auth): prevent null pointer on logout. Keep the subject line under 50 characters, and use the body to explain why, not what. Squash fixup commits before opening a PR. In production, we require all PRs to have a clean commit history—no 'WIP', 'fix typo', or 'oops' commits. Use git rebase -i to squash and reorder. This makes bisecting bugs trivial.
Opening a Pull Request: The Art of the First Impression
A good PR is more than code—it's communication. Write a descriptive title following the same convention as commits. In the description, explain what the change does, why it's needed, and how to test it. Reference related issues with Closes #123. Keep the scope narrow; a PR should do one thing. In production, we've rejected PRs that were too large to review effectively. Aim for under 400 lines changed. If your PR is larger, break it into smaller, dependent PRs. Use draft PRs for work in progress to signal that you're not ready for review.
.github/PULL_REQUEST_TEMPLATE.md. Fill it out completely—it guides you to include necessary information.Code Review: Giving and Receiving Feedback
Code review is a conversation, not a gate. When reviewing, focus on correctness, maintainability, and adherence to project standards. Be specific: 'This function could throw if data is null' is better than 'This looks wrong'. When receiving feedback, don't take it personally. Address each comment, even if it's just to explain why you disagree. In production, we use a 'resolve conversation' button only after the issue is addressed. If a reviewer requests changes, make them and re-request review. Never dismiss reviews without discussion.
Handling Merge Conflicts: The Inevitable Reality
Merge conflicts happen when two branches modify the same part of a file. The fix is to resolve them manually. Use git mergetool or an IDE. Understand both sides of the conflict—don't just keep your version. In production, we've seen conflicts that were resolved incorrectly, introducing bugs. After resolving, test the code. If you're unsure, ask the other author. To minimize conflicts, sync your branch frequently and communicate with other contributors about shared files.
CI/CD and Testing: Let the Machines Check First
Before your PR is reviewed, automated checks should run: linting, unit tests, integration tests, and security scans. Most open source projects use GitHub Actions or similar. Run these tests locally before pushing to avoid wasting CI resources. In production, we've seen PRs that failed linting because the contributor didn't run npm run lint. Always run the project's test suite. If tests fail, fix them before requesting review. CI is your first line of defense—don't ignore it.
After Merge: Clean Up and Celebrate
Once your PR is merged, delete your feature branch from your fork. This keeps your fork clean and prevents stale branches. Sync your fork's main branch with upstream. If your PR was part of a larger feature, update any related issues or documentation. In production, we automate branch deletion after merge using GitHub settings. Also, update your local repository: git checkout main && git pull upstream main && git push origin main. Then delete the local branch: git branch -d my-feature.
git branch operations. Regular cleanup is essential.Etiquette and Community: Beyond the Code
Open source is people. Be respectful in discussions, even when disagreeing. Read the project's contributing guidelines and code of conduct. If you're stuck, ask questions in the designated channels (issues, discussions, Slack). Don't demand features—propose solutions. In production, we've seen contributors get frustrated when their PR isn't reviewed immediately. Maintainers are often volunteers. Be patient and follow up politely after a reasonable time (e.g., one week). If your PR is stale, consider pinging with a friendly comment.
Advanced: Managing Multiple Forks and Upstreams
Sometimes you need to contribute to multiple forks or track several upstreams. For example, you might fork a library, then fork another project that depends on it. Use multiple remotes to manage this. Name them descriptively: upstream-lib, upstream-app. You can also use git worktree to check out multiple branches simultaneously. In production, we use a script to automate syncing all forks. Be careful with force pushes when multiple remotes are involved—you might accidentally push to the wrong one.
Common Pitfalls and How to Avoid Them
Even experienced contributors make mistakes. Common pitfalls include: forgetting to sync before creating a branch, committing directly to main, using git push --force instead of --force-with-lease, and not testing after resolving conflicts. Another is opening a PR from your fork's main branch—always use a feature branch. In production, we've seen all of these. The fix is discipline: follow the workflow every time, use scripts to automate repetitive steps, and double-check before pushing. If you mess up, don't panic—most mistakes are fixable with git reflog.
git reflog is your lifeline.git reflog and always follow the standard workflow.The 18-Month-Old PR That Could Never Be Merged
- Always keep your fork synchronized with upstream.
- Never work on your fork's main branch.
- Create a feature branch for each contribution.
- Sync before creating a new PR:
git fetch upstream && git rebase upstream/main.
git fetch upstream && git rebase upstream/main on your main branchgit rebase upstream/main| File | Command / Code | Purpose |
|---|---|---|
| fork-setup.sh | git clone https://github.com/YOUR_USERNAME/upstream-repo.git | Why Fork? The Case for Controlled Collaboration |
| sync-fork.sh | git checkout my-feature | Syncing Your Fork |
| branch-create.sh | git checkout main | Branching Strategy |
| squash-commits.sh | git rebase -i HEAD~3 | Commit Hygiene |
| pr-template.md | Fixes the login error when email field is empty. | Opening a Pull Request |
| review-comment.txt | Reviewer: Could we add a null check here? If `user` is undefined, line 42 will c... | Code Review |
| resolve-conflict.sh | git fetch upstream | Handling Merge Conflicts |
| .github | name: CI | CI/CD and Testing |
| cleanup.sh | git checkout main | After Merge |
| follow-up-comment.txt | Hi maintainers, just checking in on this PR. I've addressed all the feedback. Le... | Etiquette and Community |
| multiple-remotes.sh | git remote add upstream-lib https://github.com/another-org/lib-repo.git | Advanced |
| reflog-recovery.sh | git reflog | Common Pitfalls and How to Avoid Them |
Key takeaways
Frequently Asked Questions
20+ years shipping production infrastructure and CI/CD at scale. Written from production experience, not tutorials.
That's Git. Mark it forged?
4 min read · try the examples if you haven't