How to get a change merged into an open source project you do not run, and keep contributing after that. This guide is for anyone without merge rights; maintainers, see If you run a project. Every fact links to its source, next to the step it supports.
Last reviewed October 2026.
GitHub puts it this way: "The cost to create has dropped. The cost to review hasn't."
-
Search open and closed issues and pull requests (requests to merge a change) before you write code (step 3). A study of 21 popular GitHub projects found duplicated or superseded work the top reason pull requests were not accepted, both in their authors' reports and in a manual check of 263 of them.
-
Get a maintainer's yes before you build anything bigger than a small fix (step 3). In the same study, those authors ranked clashing with the maintainers' vision second.
-
Stay with your pull request until it merges (step 7), or say in it that you are stepping away so someone can take over. Difficulty addressing maintainers' review comments was a likely reason for 46% of 354 abandoned pull requests in a study of 10 large GitHub projects.
-
Follow coordinated vulnerability disclosure: keep suspected vulnerabilities out of public issues, pull requests and chat. Use the project's SECURITY.md channel or, on GitHub, Report a vulnerability (Security & quality tab), where enabled. With neither, open an issue asking only for a security contact. Reproduce the problem first, and report it in your own words.
-
Opening or installing an unfamiliar repository can run its code without asking. In VS Code, keep it in Restricted Mode until you have reviewed it, .vscode folder included. This npm command skips package.json install scripts until you have read them:
npm install --ignore-scripts
Run code that a stranger sends you, such as a job coding test, only in an isolated, throwaway environment like a virtual machine.
-
Keep passwords, tokens and keys in files Git ignores; check what you are about to commit (step 5). Pushed one? Revoke or rotate it first: deleting it from history is not enough.
-
Submit only work you have the right to submit. If it relates to your job or work time, ask your employer first: they may own it. Never copy in incompatibly licensed code: Apache-2.0 code cannot go into a GPLv2-only project.
-
Pushing to a public project makes your commit email public for good: commits and sign-offs record it, and where the project uses the Developer Certificate of Origin (DCO), you agree the project keeps it indefinitely. Before your first commit, keep your personal email private (step 4).
- Ground rules
- 1. Choose a project and a first task
- 2. Read the project's rules
- 3. Check nobody else is on it, and get a yes
- 4. Set up your copy
- 5. Make the change
- 6. Open the pull request
- 7. Get through review
- 8. After the decision
- If you run a project
- Go deeper
- About this guide
- Start with software you already use: you know what is broken and why it matters. Otherwise, look for good first issue labels or try Up for Grabs, which lists projects with tasks set aside for newcomers. GitHub's default help wanted label, separate from good first issue, indicates that a maintainer wants help.
- Check for a license, usually a LICENSE file: software with no license generally gives you no permission to use, modify or share it. Check that the Open Source Initiative (OSI) has approved it: source-available licenses such as the SSPL are not open source.
- Check that recent pull requests from outsiders got replies; on GitHub, Insights > Pulse shows recent activity.
- Good first tasks include fixing wrong or missing docs, confirming a reported bug on the latest version, testing an open pull request and reporting the result, and answering questions, translation and design.
- Check whether the project accepts typo-only pull requests from first-timers: Django calls a pure typo fix generally not suitable as a first contribution. Do not open pull requests just for an event: in 2026 Hacktoberfest no longer counts pull requests or merge requests toward rewards.
- Find CONTRIBUTING in the root, docs/ or .github/; GitHub links it when you open an issue or pull request. It explains how the process works and may require tests.
- Not every project takes pull requests: the Linux kernel takes plain-text email patches, and Git reviews patches on its mailing list.
- Since February 2026, GitHub lets maintainers turn pull requests off or restrict them to collaborators, and since June 2026 cap how many each contributor without write access may have open. Some projects require an approved issue before any pull request, and Ghostty automatically closes pull requests from anyone it has not vouched for.
- AI policies differ: QEMU declines code believed to include or derive from AI-generated content, the Linux kernel accepts AI-assisted work with an Assisted-by tag and a human sign-off, and Ghostty requires disclosing all AI use. CPython may block people who keep opening unproductive pull requests. With no AI policy, ask before submitting AI-assisted work.
- Some projects require a Signed-off-by line on each commit, certifying under the DCO that you may submit the work:
git commit -sadds the line, andgit rebase --signoff --keep-base upstream/mainadds it to existing commits. If you already pushed them, push withgit push --force-with-lease --force-if-includes origin my-fix, not the step 7 block. If that push refuses, ask in the pull request. - Others need a signed Contributor License Agreement (CLA), often through a bot on your first pull request. Read it before you sign: Apache's, for example, lets the foundation sublicense your contributions. On GitHub, unless a CLA or other agreement says otherwise, your contribution takes the repository's license.
- Search with the error message and function or feature names. On GitHub, delete
state:openfrom the Issues or Pull requests search box to include closed ones. - Check whether someone has claimed the issue or linked a pull request. Claim work as CONTRIBUTING says; some projects ask you not to claim at all.
- Show interest with a reaction, not a +1 comment: Ghostty asks for emoji reactions because, with GitHub's default settings, every comment emails everyone in the thread.
- For anything bigger than a small fix, describe the problem and your planned approach in the issue, and ask whether a pull request is welcome.
- Found a bug that isn't a small, obvious fix? Report it before you fix it, privately if it is a security problem (rule 4). Use any issue template. Give the steps to reproduce it, what you expected and what happened, and your platform and versions. Paste errors as text, and cut your example to the smallest that fails. Remove tokens and personal data from logs and screenshots: on GitHub, anyone can open public repositories' attachments without signing in.
You need Git and an account on the site that hosts the project. To push to GitHub, Git must sign in over HTTPS or SSH, and GitHub recommends HTTPS. Over HTTPS your GitHub password will not work: before your first push, set up GitHub CLI or Git Credential Manager, which comes with Git for Windows. On Windows, use Git Bash, part of Git for Windows: PowerShell before version 7 lacks the && used in step 7.
Then give Git your name and email, once per computer (Pro Git). On GitHub, select Keep my email addresses private under Settings > Emails and use the noreply address it gives you, which keeps your personal email private; the change affects only later commits. On GitLab, use its private commit email.
git config --global user.name "YOUR NAME"
git config --global user.email "ID+USERNAME@users.noreply.github.com"Git opens your system's default text editor for messages unless you choose another.
Without write access, fork the project, then clone your fork: a fork alone puts no files on your computer, and on GitHub or GitLab, cloning the project alone leaves you nowhere to push. With write access, skip the fork: clone the project and work on a branch.
git clone YOUR-FORK-URL
cd PROJECT
git remote add upstream PROJECT-URL
git fetch upstream
git switch -c my-fix upstream/mainThe last line starts a branch from the project's latest code. Use one per change: for the next, run the last two lines again with a new name, and use it wherever this guide says my-fix. To work on an earlier pull request again, switch back to its branch: git switch my-fix. Replace main, here and in steps 2 and 7, with the project's default branch or the one CONTRIBUTING names.
- Follow rule 5, then build exactly as the docs say, with the versions they name.
- If the project has a .devcontainer directory, use it: it defines a ready-made development environment.
- If setup fails, search the issues for the exact error, then ask where CONTRIBUTING says, giving your operating system, versions, the command and full output.
- Avoid fixing npm or pip permission errors with sudo: npm recommends installing Node with a version manager such as nvm; for Python, use a virtual environment (venv).
- Run the tests before changing anything, to know which failures were already there, and again before you push.
- One pull request, one purpose: CPython asks for one issue or one feature in each, and usually rejects reformat-only ones. Keep refactoring and reformatting out of yours, and ask before sending a cleanup pull request.
- Match the surrounding style and run the style checks CONTRIBUTING names.
- Add or update tests and docs for your change in the same pull request: CPython does not accept pull requests without tests.
- Used AI? Review its output in detail until you can explain the change in your own words, and never alter or bypass existing tests to make a failing one pass (CPython asks both).
Copy the style of the project's recent commit messages:
git log --no-merges --oneline -20Otherwise, write a summary line of no more than about 50 characters, a blank line, then why. Use the imperative: "Fix crash on empty file", not "Fixed crash". Use Conventional Commits only if asked: its specification defines only feat and fix, and projects add other types.
git diff --staged shows what you are about to commit; check it (rule 6). Add -s to git commit if the project requires a sign-off (step 2).
git add FILE
git diff --staged
git commit-
Push your branch, then, on the project's GitHub page, click Compare & pull request and choose the base branch the project names:
git push -u origin my-fix
-
In your own words, say what problem it solves and why, what changed, and how you tested it. Use any template; add before and after screenshots of visible changes.
-
Write
Fixes #123only for an issue the pull request fully fixes: merging it closes the issue on GitHub and GitLab (only if it targets the default branch) and on Codeberg, which runs Forgejo. Otherwise writeRelated to #123, which references the issue without closing it. -
Not ready? Open a draft, which cannot be merged and, by default, does not count toward the cap on open pull requests (step 2). Keep few pull requests open in one project.
-
If your fork is in your personal account, tick Allow edits from maintainers so people with write access to the project can push fixes to your branch (GitLab: Allow commits from members who can merge to the target branch). If your fork has GitHub Actions workflows, the box reads Allow edits and access to secrets by maintainers: ticking it can expose your fork's secrets and other branches; leave it off unless you accept that.
-
On GitHub, Actions workflow runs on a pull request from a fork to a public repository may wait for a maintainer's approval (by default, for first-time contributors); runs waiting over 30 days expire as failed.
-
Answer every comment with a fix or a reason, and ask specific questions about unclear requests.
-
Push fixes to the same branch; the pull request updates itself. Ask for another review after substantial changes.
-
Update your branch only when needed: on a conflict, when asked, or when item 4 or 6 sends you here. Where CONTRIBUTING says to merge instead or, like CPython, not to force-push, follow it, but run
git pull --no-rebasewhere it saysgit pull(item 4). On a conflict, remove the markers (item 5), then rungit add FILE,git merge --continueandgit push. Otherwise, replay your commits on the project's latest code, keeping a maintainer's commits but not their merges:git fetch upstream && git fetch origin && git rebase origin/my-fix && git rebase upstream/main && git push --force-with-lease origin my-fix
If someone pushed to your branch after your fetch, the block's
--force-with-leasepush refuses; then rerun the block. Plain--forcewould erase their commits. -
If
git pushis rejected, your branch has usually diverged: your fork and your computer each have commits the other lacks. Do not update it with plaingit pull, even when Git suggests it: by default it stops with "Need to specify how to reconcile divergent branches". If you just added sign-offs or squashed, push as step 2 or item 6 says; otherwise follow item 3. -
On a conflict, Git marks the clashing lines with
<<<<<<<,=======and>>>>>>>. Fix each file, deleting the markers, then rungit add FILEandgit rebase --continue; repeat until Git prints Successfully rebased, then run the block's last two lines. If that push refuses, do not rerun the block; ask in the pull request.git rebase --abortputs your branch back as it was. -
Squash only when asked: first run the block in item 3, so you have any commits others pushed; then run
git rebase -i upstream/mainand change pick to fixup on every line after the first, leaving one commit; then push withgit push --force-with-lease --force-if-includes origin my-fix. If that refuses, do not rerun the block; ask in the pull request. -
If checks fail, open the log, reproduce the failure locally and fix it; if you cannot, ask in the pull request, linking the log.
-
If nobody replies, wait as long as the project's contributing docs say (the Linux kernel: at least a week), then ping once, politely, in the same thread, not privately. Silence is often about maintainers' time: in the study behind rule 3, lack of review was a likely reason for 23% of abandonments. Waiting workflow runs (step 6), caps (step 2) and stale closes (step 8) are not verdicts on your work either.
-
If a thread turns hostile, stop replying and tell the contact in the project's code of conduct. On GitHub you can also report them to GitHub Support and, from their profile, block them.
Declined? Ask what they would accept; try something smaller or different. Closed as stale? That is often mechanical: Kubernetes' bot marks pull requests stale after 90 inactive days and closes them after 60 more. Before redoing it, ask whether the change is still wanted.
Merged? Come back: review others' pull requests (on GitHub, anyone with read access can), answer newcomers' questions and ask what to tackle next. A GitHub Blog post advises maintainers to save mentoring for those who return: "Continuity gets you mentored."
- Add an OSI-approved LICENSE; choosealicense.com helps you pick.
- Add CONTRIBUTING.md: how to propose changes, whether to ask first, test and style commands, sign-off or CLA, review times.
- Publish an AI policy.
- On GitHub, add SECURITY.md and, for a public repository, turn on private vulnerability reporting.
- Keep setup docs current; automate setup, for example with a dev container.
- Label good first issue only issues simple enough for beginners; keep issues current.
- Approve first-time contributors' GitHub workflow runs before they expire at 30 days.
- If you turn off, restrict or cap GitHub pull requests, say so in CONTRIBUTING and the README.
- Close with a reason, not silence; say how stalled pull requests get handed over.
Starting an Open Source Project covers the rest.
- Pro Git: the free Git book, for anything beyond these commands.
- How to Contribute to Open Source (Open Source Guides): more on communities and non-code work.
- First Contributions: a practice repository for rehearsing steps 4 to 6.
Licensed CC BY-SA 4.0 (see LICENSE). To report a mistake, open an issue with a source; CONTRIBUTING.md says how.