The PR workflow you learned in the previous lesson works perfectly when you have write access to the repo. But what about contributing to a repo you don't own — say, a popular Cypress plugin with a bug, or a Playwright helper missing the feature your team needs? You can't push a branch to it directly. The answer is forking: making your own copy of the repo on GitHub, working in your copy, and then opening a PR back to the original. This lesson covers the entire fork workflow plus the upstream-syncing dance that keeps your fork honest.
Fork vs clone — different things
Two concepts that sound similar but happen at different layers:
- Clone copies a repo from GitHub to your machine. Read-write locally, but pushing requires permission on the remote.
- Fork copies a repo from one GitHub account to another GitHub account (yours). You become the owner of the new copy and can push freely.
The rule of thumb: fork on GitHub, clone the fork to your laptop. You'll end up with two remotes — your fork (origin) and the original (upstream).
Why a QA engineer would fork
Three real-world reasons:
- Contributing to open-source test tools. Cypress plugins, Playwright fixtures, Postman collections, GitHub Actions for testing — most are open source. Fixing a bug or adding a feature means forking the project.
- Trying out changes safely. Want to experiment with a library's source code without breaking it for your team? Fork, hack, run. If your idea works, PR it back. If not, abandon the fork.
- Building on top of existing test frameworks. Some teams maintain forks of popular tools with company-specific patches. Knowing how forks stay in sync with upstream is essential maintenance work.
Even if you never contribute to public open source, your company likely uses forked tools internally. Understanding the workflow makes you immediately useful.
The fork workflow, end to end
Pretend you found a bug in a fictional open-source plugin cypress-extra-asserts (github.com/qatools/cypress-extra-asserts). Walk through the contribution.
Step 1 — fork on GitHub
Visit the repo page. Click Fork in the top right. GitHub creates github.com/your-username/cypress-extra-asserts — your copy. The fork has its own URL, its own issues, its own settings.
Step 2 — clone YOUR fork
git clone https://github.com/your-username/cypress-extra-asserts.git
cd cypress-extra-assertsorigin automatically points at your fork. Confirm:
git remote -vorigin https://github.com/your-username/cypress-extra-asserts.git (fetch)
origin https://github.com/your-username/cypress-extra-asserts.git (push)
Step 3 — add the original repo as upstream
This is the move beginners miss. Add a second remote pointing at the original:
git remote add upstream https://github.com/qatools/cypress-extra-asserts.git
git remote -vorigin https://github.com/your-username/cypress-extra-asserts.git (fetch)
origin https://github.com/your-username/cypress-extra-asserts.git (push)
upstream https://github.com/qatools/cypress-extra-asserts.git (fetch)
upstream https://github.com/qatools/cypress-extra-asserts.git (push)
origin is your fork. upstream is the original. The naming is convention; you could call it original or parent, but upstream is what every README and tutorial uses.
Step 4 — branch, fix, commit
git switch -c bugfix/handle-empty-array
# ...edit the source, write a test, commit...
git add src/asserts.js test/asserts.spec.js
git commit -m "Fix toIncludeAll throwing on empty arrays"Step 5 — push to YOUR fork
git push -u origin bugfix/handle-empty-arrayThe branch goes to your fork (origin), not the original. You don't have permission to push to the original.
Step 6 — open a PR from your fork to upstream
Visit your fork's GitHub page. The "Compare & pull request" banner appears. Click it. The PR creation page shows:
- base repository:
qatools/cypress-extra-asserts— the original. - base branch:
main. - head repository:
your-username/cypress-extra-asserts— your fork. - compare branch:
bugfix/handle-empty-array.
Title, describe, link to the issue if there is one, and submit. The maintainers of the original repo are now notified. They review, request changes if needed, and merge. Once merged, your fix is in the upstream main branch — and any future user of the library benefits.
Keeping your fork in sync with upstream
Your fork freezes the moment you create it. Days later, the upstream repo accumulates new commits. To keep your fork current:
git fetch upstream
git switch main
git merge upstream/main
git push origin mainWhat just happened:
git fetch upstream— download the latest commits from the original.git switch main— be on your fork's main branch.git merge upstream/main— combine the upstream's main into yours. Usually a clean fast-forward merge.git push origin main— push the synced main back to your fork on GitHub.
Run this before starting any new contribution. A stale fork is a fork that conflicts with everything.
origin vs upstream — keep them straight
Two-line mental model:
origin= your fork. You push here. You read from here when working.upstream= the original. You fetch from here. You almost never push here (you can't unless you're a maintainer).
If you accidentally push to upstream and it errors, no harm done. If you accidentally treat your fork as the source of truth and ignore upstream, you'll diverge and any PR you eventually open will be a merge-conflict mess.
The full fork workflow
A real QA contribution
You're using cypress-axe (an accessibility-testing plugin) at work. You hit a bug — it crashes when the page has zero accessibility violations. The fix is a four-line patch.
# fork github.com/component-driven/cypress-axe in browser, then:
git clone git@github.com:your-username/cypress-axe.git
cd cypress-axe
git remote add upstream git@github.com:component-driven/cypress-axe.git
git fetch upstream
git switch -c bugfix/handle-zero-violations
# ...edit, add a test, commit...
git push -u origin bugfix/handle-zero-violations
# ...open PR on GitHub from your fork to upstream main...Two days later the maintainer comments, you tweak, push again, they merge. Your name is on the changelog of a tool used by thousands of test suites worldwide. That's the open-source flywheel.
⚠️ Common mistakes
- Cloning the original repo instead of your fork. If you
git clone github.com/qatools/...instead ofgithub.com/your-username/..., you can't push branches — you don't own that repo. Always confirm withgit remote -vbefore you push:originshould be your fork. - Forgetting to add
upstream. Without an upstream remote, you can't pull updates from the original. Months later your fork is hopelessly stale, and your "fix" rebases against ancient code.git remote add upstream <original-url>is a 10-second step that pays for itself a hundred times. - Branching off a stale main.
git switch main && git switch -c bugfix/fooworks, but if yourmainhasn't synced with upstream in weeks, your branch is built on outdated code. Sync with upstream first (git fetch upstream && git merge upstream/main), then branch.
🎯 Practice task
Make a real (or simulated) open-source contribution. 30-40 minutes. No actual code change required — the goal is the workflow.
- Pick any open-source repo with an active community — try
octocat/Hello-Worldfor a no-pressure target, or a real QA tool likecypress-io/cypress-realworld-app. - Fork it (button on GitHub).
- Clone YOUR fork:
git clone https://github.com/<your-username>/<repo>.git && cd <repo>. - Run
git remote -v. Confirmoriginis your fork. - Add upstream:
git remote add upstream https://github.com/<original-owner>/<repo>.git. Rungit remote -vagain — confirm both remotes. - Sync from upstream:
git fetch upstream && git switch main && git merge upstream/main && git push origin main. Confirm it all runs cleanly. - Create a branch:
git switch -c docs/typo-fix. Make a tiny change (fix a typo in the README, add your name to a CONTRIBUTORS file if appropriate). Commit and push. - Stretch: open a PR from your fork to the original. (For pure practice, you can close it without merging — but on a real project, follow through. The maintainer's response is a great learning moment.)
That closes Chapter 3. You can now connect to GitHub, sync via push/pull/fetch, run the full PR workflow, and contribute across repository boundaries via fork. Chapter 4 zooms back into the day-to-day Git tools — stash, log, blame, reset, revert — that turn a competent Git user into a fluent one.