Part 3 of 4

With other people

Everything so far works fine alone. This page is about the part GitHub was actually built for: several people changing the same project without stepping on each other โ€” and what to do on the day you inevitably do.

1 ยท Issues: the conversation layer

An issue is a thread about something that ought to happen โ€” a bug, a task, a question, an idea. No code involved. If a pull request answers "here's my change", an issue asks "should something change, and what?"

A useful bug report has three parts, and most don't:

a bug report people can act on
Title: Search returns no results when the query has an apostrophe

Steps to reproduce
1. Go to /search
2. Type: O'Brien
3. Press Enter

Expected  Two matching records.
Actual    "No results found." Console shows a 500.

Environment  Firefox 128, macOS 15.2, staging as of 12 Mar

Things worth knowing about issues:

  • Everything is cross-linkable. Type #312 anywhere on GitHub and it becomes a link to that issue or PR. Type @someone and they get notified.
  • Closing is automatic. A pull request whose description says Closes #312 will close issue 312 when it merges, and the two stay linked in the history.
  • Labels and milestones are just tags for filtering. bug, good first issue, and help wanted are near-universal conventions โ€” the last two are how maintainers signal work suitable for newcomers.
  • Search before opening. Someone has usually reported it. Adding a detail to an existing thread is more useful than starting a second one.

2 ยท Code review, from both sides

Review is where most of GitHub's value actually lives. It's also where new contributors feel most exposed, so it's worth being deliberate about.

Receiving a review

A reviewer leaves comments on specific lines and then picks one of three verdicts:

VerdictWhat it means
ApproveGood to merge. Any comments attached are suggestions, not blockers.
CommentFeedback without a verdict โ€” questions, observations, a partial read.
Request changesSomething needs fixing before this merges. On most repos this actively blocks the merge button.

To respond: make the edits locally, commit, push to the same branch. The PR updates itself. Then reply to each thread โ€” even a "done, good catch" โ€” and use Resolve conversation to collapse the ones that are settled, so the reviewer can see at a glance what's left.

Comments are about the code, not about you. This is genuinely hard to internalise at first, and worth doing early. Experienced engineers get "request changes" on their pull requests constantly; it's a normal Tuesday, not a report card.

If a comment doesn't make sense, ask. "I'm not sure I follow โ€” do you mean X?" is a completely standard reply, and reviewers are frequently wrong or missing context.

Giving a review

  • Use the batch workflow. Click Start a review rather than Add single comment. Otherwise each comment fires its own notification and the author starts responding before you've finished reading.
  • Separate blocking from optional. A convention that costs nothing and helps enormously: prefix anything non-blocking with nit: so the author knows what actually holds up the merge.
  • Use suggestion blocks for small fixes. In a comment, a fenced block marked suggestion renders as a proposed change the author can commit with one click.
  • Ask instead of assert when you're unsure. "What happens here if the list is empty?" ages better than "this is broken."
  • Say what's good. A review that's purely a list of faults is exhausting to receive, and reviewers who note the clever bit get better pull requests over time.

3 ยท Merge conflicts, calmly

A conflict is not an error and nothing is broken. It means two people changed the same lines of the same file, and Git โ€” sensibly โ€” refuses to guess which version is correct. It's asking you a question.

Here's the whole thing, start to finish. You're merging updated main into your branch, and you both edited the same constant.

  1. Git tells you it stopped

    the conflict appears
    $ git merge origin/main
    Auto-merging src/search.js
    CONFLICT (content): Merge conflict in src/search.js
    Automatic merge failed; fix conflicts and then commit the result.
    

    Note that it says Auto-merging first. Git already resolved everything it could on its own; what's left is the genuinely ambiguous part. A conflict in one file out of thirty is completely normal.

  2. See what needs your attention

    git status, as always
    $ git status
    Unmerged paths:
      (use "git add <file>..." to mark resolution)
            both modified:   src/search.js
    
  3. Open the file and read the markers

    Git has written both versions into the file, fenced off:

    src/search.js
      const query = event.target.value;
    <<<<<<< HEAD
      pending = setTimeout(() => fetchResults(query), 300);
    =======
      pending = setTimeout(() => fetchResults(query), 500);
    >>>>>>> origin/main
    }
    
    <<<<<<< HEAD
    Everything below this line, up to the =======, is your version โ€” what's on the branch you're standing on.
    =======
    The divider. Nothing more to it.
    >>>>>>> origin/main
    Everything above this, down from the divider, is their version โ€” what's coming in.
  4. Decide, and delete the scaffolding

    You take theirs, take yours, or write a third thing that combines both. Whichever you choose, all three marker lines must be gone when you're done. The file should read as if the conflict never happened:

    resolved
      const query = event.target.value;
      pending = setTimeout(() => fetchResults(query), 300);
    }
    

    If you're unsure whose version is right โ€” and you often will be โ€” ask the other person. A thirty-second message beats silently discarding someone's work.

  5. Mark it resolved and finish the merge

    completing the merge
    $ git add src/search.js
    $ git commit
    # Git pre-writes the message; you can just save and quit
    
    $ git push
    

    Here git add means "I've handled this one". Your pull request will refresh and the This branch has conflicts banner will clear.

The escape hatch: git merge --abort. At any point mid-conflict, this puts everything back exactly as it was before you started, with nothing lost. If you're confused or in over your head, use it, take a breath, and start again. It's a normal thing to do, not an admission of defeat.

Fewer conflicts, less pain

  • Merge main into your branch often โ€” daily on anything long-lived. Ten small conflicts spread over a week are far easier than one enormous one on Friday.
  • Keep branches short. Nearly every horrifying conflict story starts with "this branch had been open for six weeks".
  • Say what you're touching. Two people rewriting the same file at once is a coordination problem, and no tool solves it for you.
  • Small conflicts can be fixed in the browser. GitHub offers a web editor on conflicted PRs; it's fine for a couple of lines and unpleasant for anything larger.

4 ยท Forks and contributing to projects you don't own

Everything so far assumed you can push to the repository. For open source โ€” and for tightly controlled repos at work โ€” you can't. You don't have write access, and you're not going to get it just to fix a typo.

A fork solves this. It's your own complete copy of the repository, under your account, that you can do whatever you like with. You push to your copy, then propose your changes back across.

The fork and pull request workflow You fork the original repository into your own GitHub account, clone your fork to your computer, push commits back up to your fork, and then open a pull request from your fork to the original repository. acme/site the original โ€” you can't push you/site your fork on GitHub your computer where you actually work fork git clone git push โ€” to your fork, which you own open a pull request
The extra hop is the whole difference. Your commits go to a repository you control, and the pull request asks the original maintainers to take them. They review it exactly as they would an internal one โ€” the fork is invisible from their side of the conversation.
the fork workflow end to end
# 1. Click "Fork" on the project's GitHub page, then:
$ git clone git@github.com:you/site.git
$ cd site

# 2. Point at the original too, so you can stay up to date
$ git remote add upstream https://github.com/acme/site.git

# 3. Work as normal
$ git switch -c fix/apostrophe-search
$ git commit -am "Escape apostrophes in search queries"
$ git push -u origin fix/apostrophe-search

# 4. GitHub shows a "Compare & pull request" banner. Click it.

You now have two remotes: origin (your fork, which you push to) and upstream (the original, which you pull from). Popular projects move quickly, so refresh yours before starting anything new:

syncing a fork
$ git switch main
$ git fetch upstream
$ git merge upstream/main
$ git push
# your fork's main now matches the original's

GitHub also puts a Sync fork button on your fork's page, which does the same thing without the terminal.

5 ยท Guardrails teams put on main

On a real project you'll run into rules that stop you doing something. They're not obstacles aimed at you โ€” they're the reason main is trustworthy. Here's what you're likely to meet.

Protected branches
Nobody can push directly to main; every change has to arrive through a pull request. Usually the very first rule a team turns on.
Required reviews
The merge button stays disabled until N people have approved. Pushing new commits often dismisses existing approvals, so expect to re-request review after changes.
Required status checks
Tests, linters, or builds must pass before merging. See GitHub Actions for what's running and how to read a failure.
CODEOWNERS
A file mapping paths to people or teams. Touch /billing/ and the billing team is automatically added as a required reviewer.
Linear history
Merge commits are disallowed, so squash or rebase is the only route in. Keeps main a straight line.
Signed commits
Commits must be cryptographically signed to prove they came from who they claim. Common in security-sensitive repos; adds a one-time key setup.

If the merge button is greyed out, GitHub will tell you exactly why โ€” read the box at the bottom of the pull request. It's always one of: a check failing, a review outstanding, a conflict, or a rule like the ones above.

6 ยท Being easy to work with

The social conventions matter as much as the commands, and they're rarely written down anywhere.

On any team

  • Keep pull requests small. A 40-line PR gets reviewed today; a 2,000-line one gets reviewed reluctantly on Thursday.
  • Don't mix a refactor into a bug fix. Two PRs, two reviews, two clean reverts if needed.
  • Never force-push a branch someone else is working on or reviewing.
  • Nudge politely after a day or two. Reviews genuinely do get lost in the notification pile.

Contributing to open source

  • Read CONTRIBUTING.md first. It exists precisely so maintainers don't have to repeat themselves.
  • Open an issue before a large PR. "Would you accept a change that does X?" saves everyone a wasted weekend.
  • Start with good first issue labels โ€” they're curated for exactly this.
  • Maintainers are usually volunteers. Slow replies aren't rudeness, and a rejected PR isn't a judgement of you.

One habit outranks all of the above: explain why. Reviewers can see what your code does โ€” they're reading it. What they can't see is the reasoning, the option you rejected, or the constraint you were working around. Put that in the pull request description and reviews get dramatically shorter.

Back to home