Forking and Contributing to Open Source

7 min read

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-asserts

origin automatically points at your fork. Confirm:

git remote -v
origin  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 -v
origin    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-array

The 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 main

What just happened:

  1. git fetch upstream — download the latest commits from the original.
  2. git switch main — be on your fork's main branch.
  3. git merge upstream/main — combine the upstream's main into yours. Usually a clean fast-forward merge.
  4. 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 of github.com/your-username/..., you can't push branches — you don't own that repo. Always confirm with git remote -v before you push: origin should 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/foo works, but if your main hasn'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.

  1. Pick any open-source repo with an active community — try octocat/Hello-World for a no-pressure target, or a real QA tool like cypress-io/cypress-realworld-app.
  2. Fork it (button on GitHub).
  3. Clone YOUR fork: git clone https://github.com/<your-username>/<repo>.git && cd <repo>.
  4. Run git remote -v. Confirm origin is your fork.
  5. Add upstream: git remote add upstream https://github.com/<original-owner>/<repo>.git. Run git remote -v again — confirm both remotes.
  6. Sync from upstream: git fetch upstream && git switch main && git merge upstream/main && git push origin main. Confirm it all runs cleanly.
  7. 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.
  8. 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.

// tip to track lessons you complete and pick up where you left off across devices.