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
| Fork | Clone | |
|---|---|---|
| Where | On GitHub, under your account | On your computer |
| Made with | The Fork button on the project's page | git clone |
| Purpose | A copy you're allowed to push to | A 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
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
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
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
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
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-byline withgit 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.