Contributing to Open Source with Git
Contributing to open source is one of the best ways to sharpen your Git skills, build a public portfolio, and give back to the tools you use every day. The Git workflow for open-source contribution is slightly different from internal team work — you will not have push access to the original repository, so you work through a fork. This guide walks you through every step, from finding a project to having your pull request merged.
Finding a Project to Contribute To
The best first contribution is to a project you already use. When you hit a bug or a missing feature in a library you depend on, that is your entry point.
Ways to find beginner-friendly issues:
github.com/explore— trending repositories with beginner-friendly issues.Search GitHub:
label:good-first-issue language:javascript— filter by language and label.Search GitHub:
label:"help wanted" is:open— maintainers explicitly asking for help.goodfirstissue.dev— curates beginner-friendly issues across thousands of projects.up-for-grabs.net— another curated list of beginner-friendly issues.Your own
node_modulesorrequirements.txt— every dependency you use is maintained by people who would welcome a fix.
Signal | Good sign | Warning sign |
|---|---|---|
Last commit | Within last 3 months | More than 1 year ago |
Issue response time | Maintainers reply within days | Issues ignored for months |
PR merge rate | Many merged PRs from external contributors | Only maintainer commits |
CONTRIBUTING.md | Detailed guide exists | No contribution guide |
Code of Conduct | CoC file present | No CoC |
Test coverage | CI runs tests on PRs | No CI, no tests |
Fork the Repository
A fork is your personal copy of a repository on GitHub. You have full write access to your fork — you can push branches, experiment, and break things without affecting the original project.
Click the Fork button on the top-right of the repository page on GitHub. GitHub creates github.com/you/project as a copy of github.com/org/project.
Clone Your Fork
Clone your fork (not the original)
# Clone YOUR fork — you have push access here git clone git@github.com:you/project.git cd project # Confirm the remote is your fork git remote -v # origin git@github.com:you/project.git (fetch) # origin git@github.com:you/project.git (push)
Add the Upstream Remote
The "upstream" is the original repository you forked from. You need it to stay in sync with new commits made by the project maintainers.
Add upstream and verify both remotes
git remote add upstream https://github.com/org/project.git git remote -v # origin git@github.com:you/project.git (fetch) # origin git@github.com:you/project.git (push) # upstream https://github.com/org/project.git (fetch) # upstream https://github.com/org/project.git (push)
Create a Feature Branch
Always branch from a fresh main
# Make sure your local main is up to date first git fetch upstream git checkout main git merge upstream/main --ff-only # Fast-forward merged # Now create your branch git checkout -b fix/typo-in-readme # Switched to a new branch 'fix/typo-in-readme'
Make Changes and Commit
Follow the project's contribution conventions exactly. Read CONTRIBUTING.md before writing a single line of code.
Run the project locally and verify it works before making changes.
Write the smallest change that addresses the issue — no refactoring unrelated code.
Add or update tests if the project has a test suite.
Follow the project's code style (run their linter if one is configured).
Use the project's commit message format — some use Conventional Commits, others have their own style.
Commit following the project conventions
git add README.md
git commit -m "docs: fix typo in installation section
'recieve' -> 'receive' in the npm install step.
Fixes #1234"
# For a larger change:
git add src/parser.js src/parser.test.js
git commit -m "fix(parser): handle empty input without throwing
Previously `parse('')` threw a TypeError. Added an early return
for empty or nullish input that returns an empty AST node.
Fixes #789"Sync with Upstream Before Pushing
Open-source projects move fast. By the time you finish your change, the upstream main may have new commits. Rebase your branch on top of the latest upstream before pushing to keep the history clean and avoid conflicts in the PR.
Fetch upstream and rebase
git fetch upstream # From https://github.com/org/project # * branch main -> FETCH_HEAD # + abc1234...def5678 main -> upstream/main git rebase upstream/main # Successfully rebased and updated refs/heads/fix/typo-in-readme. # If there are conflicts during rebase: # 1. Fix the conflict in the affected file # 2. git add <file> # 3. git rebase --continue # (or git rebase --abort to start over)
Push to Your Fork and Open a PR
Push your branch to your fork
git push origin fix/typo-in-readme # Enumerating objects: 5, done. # Counting objects: 100% (5/5), done. # Writing objects: 100% (3/3), 312 bytes | 312.00 KiB/s, done. # remote: Create a pull request for 'fix/typo-in-readme': # remote: https://github.com/org/project/pull/new/fix/typo-in-readme
GitHub will show a banner: "Compare and pull request". Click it to open the PR creation form. Fill in:
Title: concise, imperative mood. Match the project's existing PR titles.
Description: what the change does, why it is needed, how to verify it, and a link to the issue it closes (
Fixes #1234).Screenshots: for UI changes, include before/after screenshots.
Do not request reviews from maintainers manually — they monitor their repositories.
Responding to Review Comments
Maintainers are volunteers. Their review comments are almost always constructive — they want your PR to succeed. When you receive feedback:
Reply to each comment, either with the fix or a question if you do not understand.
Make the requested changes on the same branch and push new commits — GitHub will automatically update the open PR.
Do not force-push unless the maintainer explicitly asks you to squash. Force-pushing mid-review disrupts the reviewer's workflow.
Be patient. Maintainers often have day jobs and review in their spare time.
If a maintainer asks for a change you disagree with, explain your perspective politely. They have the final say.
Push a follow-up commit addressing review feedback
# After making the requested change: git add src/parser.js git commit -m "fix(parser): use nullish coalescing per review feedback" git push origin fix/typo-in-readme # GitHub PR will show the new commit automatically
Squashing Commits if Requested
Some maintainers ask you to squash multiple commits into one before merging, to keep the project history clean.
Squash last N commits into one
# Count your commits on this branch: git log upstream/main..HEAD --oneline # abc1234 fix(parser): use nullish coalescing per review feedback # def5678 fix(parser): handle empty input without throwing # Squash both into one: git rebase -i upstream/main # In the editor, mark the second commit 'squash' (or 's'): # pick def5678 fix(parser): handle empty input without throwing # squash abc1234 fix(parser): use nullish coalescing per review feedback # Save and close — Git opens the commit message editor for the merged commit. # Force push is required after a rebase that rewrites history: git push --force-with-lease origin fix/parser-empty-input
What Happens After Your PR is Merged
The maintainer merges your PR — congratulations! You are now a contributor to the project.
Your commit appears in the project's history with your name and email.
GitHub shows you as a contributor on the repository's contributors page.
Sync your fork's main to remove the divergence:
git fetch upstream && git checkout main && git merge upstream/main && git push origin main.Delete your feature branch locally and on your fork:
git branch -d fix/typo-in-readme && git push origin --delete fix/typo-in-readme.If there is a next issue you want to tackle, create a new branch from the now-updated main.
Full Command Walkthrough: Fork to Merged PR
Complete open-source contribution flow
# --- ONE-TIME SETUP --- # 1. Fork on GitHub (click Fork button on github.com/org/project) # 2. Clone your fork git clone git@github.com:you/project.git cd project # 3. Add upstream git remote add upstream https://github.com/org/project.git # --- PER CONTRIBUTION --- # 4. Sync your local main with upstream git fetch upstream git checkout main git merge upstream/main --ff-only # 5. Create a feature branch git checkout -b fix/issue-789-empty-input # 6. Make changes # ... edit files ... git add src/parser.js src/parser.test.js git commit -m "fix(parser): handle empty input without throwing Fixes #789" # 7. Sync with upstream before pushing git fetch upstream git rebase upstream/main # 8. Push to your fork git push origin fix/issue-789-empty-input # 9. Open PR on GitHub (follow the banner or go to /compare) # 10. Address review comments git add src/parser.js git commit -m "fix(parser): handle null case per review" git push origin fix/issue-789-empty-input # 11. PR is merged! Clean up. git checkout main git fetch upstream git merge upstream/main --ff-only git push origin main git branch -d fix/issue-789-empty-input git push origin --delete fix/issue-789-empty-input