Forks and Contributing to Open Source

You can't push to a project you don't belong to. Instead, you fork it — make your own copy on GitHub — push your changes there, and open a pull request back to the original. This lesson covers the fork workflow and keeping your fork in sync.

Forks vs. clones

ForkClone
WhereOn GitHub, under your accountOn your computer
Made withThe Fork button on the project's pagegit clone
PurposeA copy you're allowed to push toA copy to work in

In practice you do both: fork the project on GitHub, then clone your fork to your computer.

Setting up

Sam forks cookbook-org/cookbook on GitHub, which creates sam-rivera/cookbook, then clones the fork:

$
git clone https://github.com/sam-rivera/cookbook.git
cd cookbook
Output
Cloning into 'cookbook'...
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 4 (delta 0), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (4/4), done.

origin points at Sam's fork. Add the original project as a second remote, by convention called upstream, to receive its updates:

$
git remote add upstream https://github.com/cookbook-org/cookbook.git
git remote -v
Output
origin	https://github.com/sam-rivera/cookbook.git (fetch)
origin	https://github.com/sam-rivera/cookbook.git (push)
upstream	https://github.com/cookbook-org/cookbook.git (fetch)
upstream	https://github.com/cookbook-org/cookbook.git (push)

You push to origin (your fork) and fetch from upstream (the original).

Making a contribution

Always work on a branch, never on your fork's main — that keeps main a clean mirror of upstream and lets you have several contributions open at once:

$
git switch -c add-waffles
echo "Waffles: flour, milk, eggs, butter" > waffles.txt
git add waffles.txt
git commit -m "Add waffle recipe"
git push -u origin add-waffles
Output
Switched to a new branch 'add-waffles'
[add-waffles c868507] Add waffle recipe
 1 file changed, 1 insertion(+)
 create mode 100644 waffles.txt
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 348 bytes | 348.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/cookbook.git
 * [new branch]      add-waffles -> add-waffles
branch 'add-waffles' set up to track 'origin/add-waffles'.

On GitHub, open a pull request from sam-rivera:add-waffles into cookbook-org:main. The maintainers review it like any PR (lesson 36). By default they can also push small fixes to your branch, which speeds things up.

Keeping your fork up to date

The original project keeps moving while you work. To bring its changes into your fork:

$
git switch main
git fetch upstream
git merge --ff-only upstream/main
git push origin main
Output
Switched to branch 'main'
Your branch is up to date with 'origin/main'.
remote: Enumerating objects: 4, done.
remote: Counting objects: 100% (4/4), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
Unpacking objects: 100% (3/3), 318 bytes | 318.00 KiB/s, done.
From https://github.com/cookbook-org/cookbook
 * [new branch]      main       -> upstream/main
Updating 39289bd..ee3ff85
Fast-forward
 omelette.txt | 1 +
 1 file changed, 1 insertion(+)
 create mode 100644 omelette.txt
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 338 bytes | 338.00 KiB/s, done.
Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/cookbook.git
   39289bd..ee3ff85  main -> main

Your local main and your fork now match upstream. If your feature branch needs the new commits too, rebase it onto main (or merge main into it):

$
git switch add-waffles
git rebase main
git push --force-with-lease
Output
Switched to branch 'add-waffles'
Your branch is up to date with 'origin/add-waffles'.
Successfully rebased and updated refs/heads/add-waffles.
Enumerating objects: 4, done.
Counting objects: 100% (4/4), done.
Delta compression using up to 10 threads
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 315 bytes | 315.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
To https://github.com/sam-rivera/cookbook.git
 + c868507...ab21c85 add-waffles -> add-waffles (forced update)

GitHub also has a Sync fork button on your fork's page, which updates its main on the server; you then git pull locally.

Before you contribute

  • Read CONTRIBUTING.md (if there is one) — it explains the project's conventions, how to run tests, and how PRs are reviewed.
  • Check the issues. For anything bigger than a small fix, comment on an issue or open one first, so you don't spend days on something the maintainers won't accept. Labels such as good first issue mark beginner-friendly tasks.
  • Follow the code of conduct and be patient: maintainers are often volunteers.
  • Keep the PR focused — one fix or feature, with tests and a clear description.
  • Licensing — by contributing you're offering your change under the project's license. Some projects ask you to sign a contributor agreement or add a Signed-off-by line with git commit -s.

Forks inside a team

Many companies give developers direct push access and use branches instead of forks. Forks are most common in open source, and in teams that want every change to come from outside the main repository.

Try it yourself

Fork a small public practice repository (or one of your own under a second account or organisation). Clone your fork, add an upstream remote, make a change on a branch and push it. Then practise syncing: git fetch upstream, fast-forward main, and push it to your fork.