66  66. Sharing a Repository: Collaborators and Forks

Until now, only you pushed to your GitHub repository. This page covers the two ways to work on a repository with other people - collaborators on one shared repository, and forks - and the habits that keep a shared project running smoothly.

66.1 Two ways to work together

Collaborators (one shared repository) Fork (your own copy)
What it is The owner invites people; everyone pushes to the same repository You copy someone else’s repository into your own GitHub account
Who can push to the original? Every collaborator Only its owners; you propose changes with a pull request
Typical use A class team project, a small team Open-source projects, starter code from a course or tutorial, any repository you can’t push to

66.2 Adding collaborators

Maria (GitHub username maria-lopez) wants Sam to work on her git-tutorial-project. On GitHub she:

  1. Opens the repository and clicks Settings (under the repository name).
  2. In the Access section of the sidebar, clicks Collaborators.
  3. Clicks Add people, types Sam’s GitHub username, picks him from the list, and confirms (Add NAME to REPOSITORY).

Sam receives an email invitation. Only after he accepts it can he push to the repository. (If you sign in to Git with a personal access token: GitHub doesn’t let fine-grained tokens push to a repository where you are a collaborator, so use browser sign-in or a classic token instead.) Then he clones it, just as on Cloning and Pulling:

git clone https://github.com/maria-lopez/git-tutorial-project.git

Output (progress lines omitted):

Cloning into 'git-tutorial-project'...

Sam now has the full project and history, with origin pointing at Maria’s repository. Every collaborator follows the same routine:

  1. git pull before you start, so you have everyone’s latest work.
  2. Work and commit (ideally on a branch, as on the next page).
  3. git pull again just before you push.
  4. git push.

66.3 When your push is rejected

On Cloning and Pulling you saw that GitHub rejects a push when it has commits you don’t have, and that the fix is git pull, then git push. On a team this happens all the time, because everyone pushes to the same main.

Sam adds a “Team” section to the README, commits (“Add team section to README”), and pushes. Meanwhile Maria, who hasn’t pulled, adds max-width to css/style.css, commits (“Limit page width”), and pushes:

git push

Output (the hint: lines that follow are omitted):

To https://github.com/maria-lopez/git-tutorial-project.git
 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/maria-lopez/git-tutorial-project.git'

The two main branches have simply moved apart:

On GitHub:          A---B---S     (S = Sam's commit)
On Maria's laptop:  A---B---L     (L = Maria's commit)

A push may only add commits on top of what is already on GitHub. Pushing Maria’s main as it is would replace S with L and throw away Sam’s work, so Git refuses. Maria pulls and then pushes:

git pull
git push

Output (progress lines omitted):

From https://github.com/maria-lopez/git-tutorial-project
   cd24c15..6ea40b6  main       -> origin/main
Merge made by the 'ort' strategy.
 README.md | 5 +++++
 1 file changed, 5 insertions(+)
To https://github.com/maria-lopez/git-tutorial-project.git
   6ea40b6..333e3a2  main -> main

Because both sides had new commits, and Git is set to merge when it pulls (the pull.rebase false setting from Installing and Configuring Git), the pull created a merge commit M. In between, Git opened the editor with the message Merge branch 'main' of https://github.com/maria-lopez/git-tutorial-project; Maria saved and closed it (in nano: Ctrl+O, Enter, Ctrl+X). Sam’s commit is now part of her main, so the push could simply add to GitHub’s branch:

A---B---L---M     <- main (Maria)
     \     /
      '-S-'       <- Sam's commit, from GitHub

If Sam and Maria had changed the same lines, the pull would have stopped with a conflict; she would resolve it as on Merge Conflicts and then push.

You may see either of two rejection messages. Both mean “GitHub has commits that your branch doesn’t include yet”, and both are fixed by pulling first:

Message What it means
! [rejected] main -> main (fetch first) GitHub has commits you have never downloaded.
! [rejected] main -> main (non-fast-forward) You downloaded them (with git fetch), but you haven’t merged them into your branch yet.

66.4 git fetch vs. git pull

git fetch downloads new commits from GitHub and updates origin/main (Git’s record of GitHub’s main), but it doesn’t change your files or your own main. git pull is a fetch followed by a merge (or a rebase, if you configured that).

Command Downloads new commits? Changes your branch and files?
git fetch Yes (updates origin/main) No
git pull Yes Yes

Fetching first lets you look before you merge. After Maria’s push, Sam runs:

git fetch
git status

Output (progress lines omitted):

From https://github.com/maria-lopez/git-tutorial-project
   6ea40b6..333e3a2  main       -> origin/main
On branch main
Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.
  (use "git pull" to update your local branch)

nothing to commit, working tree clean

git log --oneline --graph --all would now show the two new commits on origin/main. When he is ready, Sam merges them, which is exactly the second half of a pull:

git merge origin/main

Output:

Updating 6ea40b6..333e3a2
Fast-forward
 css/style.css | 1 +
 1 file changed, 1 insertion(+)

Instead of creating a merge commit, git pull --rebase takes your new local commits and replays them on top of the commits it downloaded, so history stays one straight line. Before (you committed L locally while a teammate pushed T):

      .---L          <- main (your laptop)
     /
A---B---T            <- origin/main

After git pull --rebase:

A---B---T---L'       <- main
        ^
   origin/main

L' has the same changes as L but a new commit ID. Git prints Successfully rebased and updated refs/heads/main., and you can push. To make rebasing the default, set git config --global pull.rebase true instead of false.

Rebasing rewrites the commits being replayed, so only rebase commits that nobody else has yet (your unpushed local commits, which is exactly what git pull --rebase does). If a rebase hits a conflict, fix the file, run git add, then git rebase --continue (or git rebase --abort to undo it).

66.5 Forks

A fork is your own copy of someone else’s repository, made on GitHub and stored under your account. You can push to your fork freely; to suggest your changes to the original project, you open a pull request (next page). The original repository is usually called upstream.

A fork is not the same as a clone: a fork is a copy on GitHub; a clone is a copy on your computer. Usually you do both:

   ORIGINAL-OWNER/REPO on GitHub      <-- the original ("upstream")
        |                   ^
        | 1. Fork           | 5. Pull request
        v                   |
   YOUR-USERNAME/REPO on GitHub       <-- your fork ("origin")
        |                   ^
        | 2. git clone      | 4. git push
        v                   |
   REPO folder on your computer       <-- 3. branch, edit, commit

To fork, open the original repository on GitHub, click Fork, choose your account as the owner, and click Create fork. (The tutorial step Fork the repo and clone it does exactly this with the tutorial’s starter code.) Then clone your fork and add a second remote, conventionally named upstream, that points at the original:

git clone https://github.com/YOUR-USERNAME/REPO.git
cd REPO
git remote add upstream https://github.com/ORIGINAL-OWNER/REPO.git
git remote -v

Output (progress lines omitted):

Cloning into 'REPO'...
origin  https://github.com/YOUR-USERNAME/REPO.git (fetch)
origin  https://github.com/YOUR-USERNAME/REPO.git (push)
upstream    https://github.com/ORIGINAL-OWNER/REPO.git (fetch)
upstream    https://github.com/ORIGINAL-OWNER/REPO.git (push)

Your fork doesn’t update itself when the original changes. To bring in the original’s new commits, fetch from upstream, merge, and push to your fork:

git fetch upstream
git switch main
git merge upstream/main
git push

On GitHub, the Sync fork button on your fork’s page (then Update branch) does the same for the fork on GitHub; afterwards, run git pull on your computer. If you use the GitHub CLI, gh repo fork ORIGINAL-OWNER/REPO --clone creates the fork and clones it in one step (run git remote -v afterwards to see the remotes it set up).

“Your main branch isn’t protected”

On your own repositories and forks, GitHub may show a banner: “Your main branch isn’t protected. Protect this branch from force pushing or deletion, or require status checks before merging.” It is not an error, and nothing is wrong with your repository. GitHub is suggesting a safety setting, a ruleset, that stops anyone, including you or an AI agent, from deleting main, force-pushing over it or pushing to it without a pull request. You can dismiss it for a practice project. To turn the protection on, see “Protecting main” in Pull Requests and Code Review.

66.6 Team etiquette

  • Pull before you start; push when you finish. The longer you wait, the further your copy drifts from everyone else’s.
  • Small commits with clear messages. Your teammates have to read them.
  • Use branches and pull requests instead of everyone committing straight to main (see Pull Requests and Code Review).
  • Never commit secrets such as API keys (see .gitignore and Keeping Secrets Out of Git).
Never force-push a shared branch

When a push is rejected, you may find advice (sometimes from an AI) to run git push --force. A force push makes GitHub’s branch match yours exactly, deleting any commits on GitHub that you don’t have, such as your teammate’s work from this morning. On a shared branch like main, always pull and then push instead. The variant git push --force-with-lease refuses if GitHub’s branch changed since you last fetched, but it still rewrites shared history. Repository owners can protect main on GitHub with a ruleset that blocks force pushes (see Pull Requests and Code Review).

66.6.1 AI agents in a shared repository

As of Oct 6, 2026 · Official docs: Manage multiple agents with agent view · How Claude remembers your project

Claude Code can run Git commands, including git push, so it should follow your team’s rules too. When Claude Code works in a background session in its own worktree, its built-in instructions are to commit its work and push its branch (opening a draft pull request when the task calls for it), but never to push to main or master, force-push, or merge. Write your team’s own rules in the project’s CLAUDE.md (for example “never push to main; never force-push”). The docs say your Git instructions take precedence: for example, if CLAUDE.md says you handle committing or pushing yourself, Claude leaves Git to you. Review what an agent changed before anything reaches a shared branch.

66.7 Key takeaways

  • Collaborators share one repository and can all push; a fork is your own copy of someone else’s repository, and you contribute back with pull requests.
  • A rejected push (“fetch first” or “non-fast-forward”) means GitHub has commits you don’t. Fix it with git pull, then git push.
  • git fetch only downloads; git pull downloads and merges (or rebases).
  • In a fork, origin is your fork and upstream is the original; update with git fetch upstream and git merge upstream/main.
  • Never force-push a shared branch.

Practice Questions

Easy

Question 1 · Multiple choice

In which situation do you need a fork rather than being added as a collaborator?

  1. You and two classmates are building your team’s project in a repository one of you owns.
  2. You want to contribute a fix to a public open-source project whose owners you don’t know.
  3. You want to work on your own repository from a second computer.
  4. You want to see the history of your own project.

b. You can’t push to a stranger’s repository, so you fork it, push to your fork, and open a pull request. A team project (a) is the classic case for collaborators. Working from a second computer (c) only needs git clone and git pull, and viewing history (d) needs git log.

Question 2 · True or false

As soon as Maria adds Sam as a collaborator on GitHub, Sam can push to her repository, even before he responds to the invitation.

False. Sam receives an invitation and gets collaborator access only after he accepts it.

Question 3 · Matching

Match each command with what it does.

Command What it does
1. git clone URL A. Download new commits and merge them into your current branch
2. git fetch B. Send your new commits to the remote
3. git pull C. Copy a repository from GitHub to your computer
4. git push D. Connect your clone to the original repository of a fork
5. git remote add upstream URL E. Download new commits without changing your files or branch

1 → C, 2 → E, 3 → A, 4 → B, 5 → D.

Question 4 · Short answer

What is origin/main?

It is Git’s record (on your computer) of where the main branch on the remote origin (GitHub) was the last time you talked to GitHub with git fetch, git pull, or git push. It doesn’t update by itself.

Question 5 · True or false

git fetch changes the files in your working directory.

False. git fetch only downloads new commits and updates origin/main. Your files and your own main stay the same until you merge (or pull).

Question 6 · Short answer

You forked a project and cloned your fork. By convention, what do the remotes origin and upstream point to?

origin points to your fork on GitHub (YOUR-USERNAME/REPO), because that is what you cloned. upstream points to the original repository (ORIGINAL-OWNER/REPO), which you add with git remote add upstream URL.

Medium

Question 7 · Read the output

Maria runs git push and sees:

 ! [rejected]        main -> main (fetch first)
error: failed to push some refs to 'https://github.com/maria-lopez/git-tutorial-project.git'

What happened, and what should she do?

Someone (here, Sam) pushed commits to main on GitHub that Maria doesn’t have. Her push would have thrown them away, so Git refused. She should run git pull to download and merge Sam’s commits (resolving any conflict), then run git push again.

Question 8 · Write the command

Write the commands Maria uses to fix the rejected push in Question 7, and describe what might happen between the two commands.

git pull
git push

Between them, the pull downloads Sam’s commit and merges it. Because both sides have new commits, it creates a merge commit and opens the editor for its message (accept it). If both of them changed the same lines, the pull stops with a merge conflict instead, which she must resolve (edit the file, git add, git commit) before pushing.

Question 9 · Read the output

After running git fetch, Sam sees this in git status:

Your branch is behind 'origin/main' by 2 commits, and can be fast-forwarded.

What does this mean, and which command brings his main up to date?

GitHub’s main has two commits that his local main doesn’t, and he has no new commits of his own, so a fast-forward is possible. git merge origin/main (or simply git pull) moves his main forward to match.

Question 10 · Compare

A rejected push can end with (fetch first) or with (non-fast-forward). What is the difference between the two situations, and what do you do in each?

  • (fetch first): GitHub has commits that your computer has never downloaded.
  • (non-fast-forward): you have already downloaded them (for example with git fetch, so origin/main knows about them), but your branch doesn’t include them yet.

The fix is the same in both cases: git pull (merging GitHub’s commits into your branch, resolving any conflicts), then git push.

Question 11 · Write the command

Your fork already has an upstream remote. The original project has new commits on main. Write the commands to bring them into your local main and then update your fork on GitHub.

git fetch upstream
git switch main
git merge upstream/main
git push

git push sends the result to origin, which is your fork. (Alternatively, use Sync fork on GitHub and then git pull.)

Question 12 · Ordering

Put these steps for contributing to an open-source project in order:

  • A. Push your branch to your fork
  • B. Click Fork on the original repository
  • C. Open a pull request to the original repository
  • D. Clone your fork to your computer
  • E. Create a branch and commit your changes

B → D → E → A → C. Fork on GitHub, clone your fork, work on a branch, push the branch to your fork (origin), then open a pull request asking the original project to merge it.

Question 13 · Multiple choice

Which statement about git pull is correct?

  1. It uploads your commits to GitHub.
  2. It downloads new commits and then merges (or rebases) them into your current branch.
  3. It only works in forked repositories.
  4. It deletes your local commits and replaces them with GitHub’s.

b. A pull is a fetch followed by a merge (or a rebase, if configured). Uploading is git push (a). Pull works with any remote (c), and it keeps your local commits (d); it combines them with the downloaded ones.

Hard

Question 14 · Short answer

Explain why Git refuses a push when GitHub’s main has a commit you don’t have. What would happen to that commit if Git allowed the push?

A normal push may only add commits on top of the branch on GitHub (a fast-forward). Your main doesn’t contain the other person’s commit, so making GitHub’s main equal to yours would drop that commit from the branch - your teammate’s work would disappear from main. Git refuses so that you integrate the work first (git pull) and then push a history that includes both.

Question 15 · Scenario

Sam’s push to main is rejected. A classmate says, “Just use git push --force, it always works.” Explain what would happen to the team and what Sam should do instead.

A force push makes GitHub’s main exactly match Sam’s local main, so every commit on GitHub that Sam doesn’t have - for example Maria’s latest work - is removed from the branch. Teammates’ later pulls would get confusing results, and the work may be lost. Instead Sam should run git pull (resolving any conflicts) and then a normal git push. Teams can also protect main on GitHub so force pushes are blocked.

Question 16 · Compare

Compare git pull (with merge) and git pull --rebase. What does each do with your local commits, and why is git pull --rebase considered safe while rebasing commits you already pushed is not?

  • Merge: keeps your local commits as they are and adds a merge commit that joins them with the downloaded commits. History shows a split and a join.
  • Rebase: replays your local commits on top of the downloaded ones, giving a straight history without a merge commit. The replayed commits get new IDs.

Rebasing changes commit IDs. With git pull --rebase, only your unpushed commits are replayed - nobody else has them yet, so nothing breaks. Rebasing commits others already have would give them different versions of “the same” commits and cause confusion and duplicated work.

Question 17 · Read the output

Sam sees this in git status:

Your branch and 'origin/main' have diverged,
and have 1 and 1 different commits each, respectively.

Explain what “diverged” means here and what Sam should do before pushing.

Both sides moved since they last matched: Sam’s main has 1 commit that GitHub doesn’t have, and GitHub’s main has 1 commit that Sam doesn’t have. A push would be rejected (non-fast-forward). He should run git pull (merge, or git pull --rebase) to combine the two lines of work, resolve any conflicts, and then git push.

Question 18 · Long answer

You are setting up a shared repository for a three-person student team that will also use Claude Code. Write five short team rules for working with Git and GitHub, and explain one of them.

A model answer:

  1. Pull before you start and before you push.
  2. Do your work on a branch and merge it through a pull request; nobody commits directly to main.
  3. Never force-push a shared branch (and protect main on GitHub).
  4. Keep commits small with clear messages; never commit secrets.
  5. Put these rules in CLAUDE.md and review everything an AI agent changes before it is pushed or merged.

Explanation (for example, rule 3): a force push replaces GitHub’s branch with yours and silently removes teammates’ commits, so the team agrees to pull and merge instead.

Question 19 · Scenario

Your team lets Claude Code work on tasks in background sessions in your shared repository. According to the Claude Code docs (as of Oct 2026), what does such a session do with its finished work, which Git actions does it avoid, and how can your team add its own Git rules?

A background session that edited files in its own worktree commits its work and pushes its branch (and may open a draft pull request when the task calls for it). It never pushes to main or master, never force-pushes, and never merges. The team can write its own Git rules in the project’s CLAUDE.md; the docs say your Git instructions take precedence. People still review the work before it is merged.

© 2025-2026 Y. Rosenthal. All rights reserved.