Git Course 0%

Forks and the fork workflow

Intermediate Core Git GitLab UI GitHub UI ≈ 9 min

What you will learn

  • What a fork is and when a team needs one
  • The two-remote setup: origin for your fork, upstream for the original
  • How to keep a fork current and open a merge request across repositories

After this lesson you can

  • I can contribute a change to a project where I have read access only

Why this matters

Most of your work will be on projects where you are a member and can push branches directly. Sooner or later you will want to fix something in a project where you are not: another team's documentation, an internal library, an open-source tool. A fork is how that works, and it is the standard route for every open-source contribution ever made.

What a fork is

Teams use forks in three situations: contributing to a project you cannot push to, a project that accepts contributions from strangers, and organizations whose policy is that nobody pushes branches to the main repository.

The setup

The fork workflow Three repositories. The original project, which you cannot push to, is forked into your own copy on the server; you clone your fork to your computer. You push branches to your fork, then open a merge request from your fork to the original. To stay current you add the original as a second remote called upstream and fetch from it, merging its main into yours. Original project northwind/trailguide you cannot push here Your fork you/trailguide yours; push freely Your computer origin → your fork upstream → original fork git push git clone git fetch upstream merge request Your fork is the only place you push. The merge request is the request to accept your branch into the original.
You push only to your fork; the merge request crosses from the fork to the original. `upstream` is how you stay current.

The fork itself is one button on the website: Fork on GitLab (top right of the project page) or Fork on GitHub (top right of the repository). Then, on your computer:

Terminal
$ git clone https://gitlab.com/you/trailguide.git
$ cd trailguide
$ git remote add upstream https://gitlab.com/northwind-trails/trailguide.git
$ git remote -v
origin	https://gitlab.com/you/trailguide.git (fetch)
origin	https://gitlab.com/you/trailguide.git (push)
upstream	https://gitlab.com/northwind-trails/trailguide.git (fetch)
upstream	https://gitlab.com/northwind-trails/trailguide.git (push)

Two remotes, and the naming is a universal convention:

  • origin is your fork. You push here.
  • upstream is the original. You fetch from here and never push.

Working

The daily loop is the same as always, with one extra step at the start:

Terminal
$ git fetch upstream
$ git switch main
$ git merge upstream/main
$ git switch -c docs/12-windows-install-steps

Fetching upstream brings in what the original project has done since you forked, and merging it into your main keeps your fork current. Then branch, work, commit, and push to your fork:

Terminal
$ git push -u origin docs/12-windows-install-steps

Finally open the merge request from your fork's branch to the original project's main. Both platforms detect the relationship and preselect it.

How to do it

Terminal
$ git clone <your-fork-url>
$ cd <project>
$ git remote add upstream <original-url>
$ git fetch upstream
$ git switch main && git merge upstream/main
$ git switch -c fix/12-something
$ git push -u origin fix/12-something

Then open the merge request on the website. The only difference from normal work is the extra remote and remembering that origin is your copy.

Common mistakes

  • Cloning the original instead of your fork. Then origin points at a repository you cannot push to, and your first push is refused with a permission error. Check git remote -v: origin must be your copy.
  • Pushing to upstream. It will be refused, correctly. Only ever git push origin.
  • A stale fork. Your main still shows the state at fork time, and branches started from it conflict. Sync before each new branch.
  • Opening the merge request in the wrong direction. The form has two sides; the target is the original project.
  • Expecting the fork to update itself. It does not; that is what Sync fork and git merge upstream/main are for.

Try it yourself

Goal: rehearse the two-remote setup, using two projects of your own.

  1. On GitLab or GitHub, create a second empty private project called trailguide-original and push your playground into it. Treat it as "the original".
  2. Use the platform's Fork button to fork it into a copy (on GitHub, forking your own repository requires a different owner or a different name; if that is blocked, create trailguide-fork manually and push the same content, which is equivalent for the exercise).
  3. Clone the fork, add the original as upstream, and run git remote -v.
  4. Branch, make a change, push to origin, and open the merge request from the fork to the original.

Expected result: git remote -v lists four lines, two for origin (your fork) and two for upstream (the original), and the merge request form shows two different projects.

Show solution

The important habit is the check in step 3: whenever a push is refused with a permission error on a fork workflow, git remote -v almost always shows that origin is the original rather than the fork. git remote set-url origin <your-fork-url> fixes it.

Check yourself

1. In a fork workflow, where do you push your branches?
2. What does git fetch upstream followed by git merge upstream/main do?
3. Your push to a fork is refused with a permission error. What should you check first?

Key terms

Fork Remote origin Upstream Merge request (MR) Pull request (PR)