Pushing & Pulling Commits in Git

17 min read·Jan 1, 2025

In Git, pushing, pulling, and fetching refer to three correlated operations used to keep local and remote repositories in sync, where:

  • Pushing means uploading the changes made in a local repository to a remote repository, making them available to other developers.
  • Pulling means downloading and integrating the changes made by you or other developers to a remote repository into a local repository.
  • Fetching means downloading the changes made to a remote repository without integrating them.

Local vs remote branches

A local branch is a branch that only exists on your local copy of the Git repository, and that only you can view, create, modify, and delete.

A remote branch, on the other hand, is a branch that exists on the remote repository, whose commits can be downloaded (i.e. pulled) and updated (i.e. pushed) by you and other developers, and that serves as a reference point for local branches.

In practice, local branches are usually connected to remote branches, then called upstream branches, which allow developers to push and pull commits to and from the remote repository, ensuring that everyone's work is synchronized.

Checking if a branch is "ahead" or "behind"

When working with remote repositories, it is very common for local branches to get out of sync with their corresponding upstream branches.

When the commits of a local branch have not yet been pushed to the remote repository, the local branch is then considered ahead of the remote branch, as it has more commits.

On the other hand, when the commits of a remote branch have not been pulled into the local branch, the local branch is then considered behind the remote branch, as it has less commits.

To check whether a local branch is ahead of, behind, or in sync with its upstream branch, you can use the git status command:

$ git status
On branch main
Your branch is ahead of 'origin/main' by N commit.
  (use "git push" to publish your local commits)

nothing to commit, working tree clean

Listing upstream branches

To output a list of all remote branches, you can use the git branch -r command:

$ git branch -r
  <origin>/<branch>

On the other hand, to output a list of all local branches and their connected upstream branches (if they have one), you can use the git branch -vv command:

$ git branch -vv
* <local> <hash> [<remote>: <status>] <message>

Where:

  • * indicates the checked out branch.
  • <local> is the name of the local branch.
  • <hash> is the latest commit hash on that branch.
  • <remote> is the name of the upstream branch.
  • <status> is the number of commits ahead or behind.
  • <message> is the commit message of the latest commit on that branch.

Example

This command will list 2 local branches:

$ git branch -vv
* main    3975fbd [origin/main: ahead 1] refactor: update server port
  develop 8821236 refactor: add healthcheck route

Where:

  • The local main branch is ahead of the remote origin/main branch by 1 commit.
  • The local develop branch is not connected to any upstream branch.

Pushing commits to a remote repository

To push the local commits of a branch to a remote repository, you can use the git push command:

$ git push <origin> <branch>

Where:

  • <origin> is the name of the remote repository you cloned (e.g., origin).
  • <branch> is the name of the remote branch you want to push the local commits to (e.g., feature).

Note: If the specified upstream branch doesn't exist on the remote repository, it will be automatically created.

Example

This command will:

  1. Create a new upstream branch named develop on the remote repository named origin.
  2. Upload the latest commits to it.
$ git push origin develop
Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 8 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 361 bytes | 361.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (1/1), completed with 1 local object.
remote:
remote: Create a pull request for 'develop' on GitHub by visiting:
remote:      https://github.com/learnbackend/git-lesson-test/pull/new/develop
remote:
To github.com:learnbackend/git-lesson-test.git
* [new branch]      develop -> develop

Tracking a remote branch

By default, Git doesn't keep track of the relationships between local and remote branches.

When pushing a local branch to a remote repository for the first time, you can tell Git to track (or associate) the local branch to the remote branch using the --set-upstream flag:

$ git push --set-upstream <origin> <branch>

Once the upstream branch is set, you will no longer need to specify the remote nor the checked out branch name every time you push commits:

$ git push

On the other hand, to set or change the upstream branch of a local branch, you can use the git branch command as follows:

$ git branch --set-upstream-to <origin>/<branch>

Example

This command will:

  1. Create a new upstream branch named develop on the remote repository named origin.
  2. Upload the latest local commits.
  3. Set a tracking relationship between the local develop branch and the remote origin/develop branch.
$ git push --set-upstream origin develop
Enumerating objects: 8, done.
Counting objects: 100% (8/8), done.
Delta compression using up to 8 threads
Compressing objects: 100% (6/6), done.
Writing objects: 100% (6/6), 689 bytes | 689.00 KiB/s, done.
Total 6 (delta 2), reused 0 (delta 0), pack-reused 0
remote: Resolving deltas: 100% (2/2), completed with 1 local object.
remote:
remote: Create a pull request for 'develop' on GitHub by visiting:
remote:      https://github.com/learnbackend/git-lesson-test/pull/new/develop
remote:
To github.com:learnbackend/git-lesson-test.git
* [new branch]      develop -> develop
branch 'develop' set up to track 'origin/develop'.

Pulling commits from a remote repository

To pull the latest commits from a remote branch into a local branch, which implies both fetching and merging, you can use the git pull command:

$ git pull [<origin> <branch>]

Where:

  • <origin> is the name of the remote repository you cloned.
  • <branch> is the name of the remote branch you want to pull the commits from.

Note: If the remote branch is already tracked, you can use the git pull command without arguments.

💡 Tip: It is considered good practice to first pull the latest commits from the remote branch before starting to work on the local branch in order to avoid creating unnecessary merge conflicts.

Fetching upstream branches

To download a copy of all upstream branches on your local machine without actually merging their commits locally, you can use the git fetch command:

$ git fetch

This allows you to first inspect the changes that have been made upstream before deciding how to integrate them, thus avoiding potential losses or conflicts.

Alternatively, to fetch the commits of a specific upstream branch, you can use the following syntax:

$ git fetch <origin> <branch>

Example

This command will fetch the missing origin/develop upstream branch:

$ git fetch
From github.com:learnbackend/git-lesson-test
 * [new branch]      develop    -> origin/develop

This command will only download the latest commits of the upstream origin/develop branch:

$ git fetch
remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 3 (delta 1), reused 3 (delta 1), pack-reused 0 (from 0)
Unpacking objects: 100% (3/3), 369 bytes | 184.00 KiB/s, done.
From github.com:learnbackend/git-lesson-test
   8821236..fb48a56  develop    -> origin/develop

Creating a local branch from an upstream branch

When cloning a remote repository or fetching remote branches, Git only creates a local branch for the default branch (usually main or master).

To create a local branch from an upstream branch, you can use the git switch -c command as follows:

$ git switch -c <local> <origin>/<remote>

Example

This command will first fetch locally the upstream branch origin/develop

$ git fetch
From github.com:learnbackend/git-lesson-test
 * [new branch]      develop    -> origin/develop

Then, this command will create a new local branch named develop based off the upstream branch origin/develop:

$ git switch -c develop origin/develop
branch 'develop' set up to track 'origin/develop'.
Switched to a new branch 'develop'

Finally, this command will check the current state of local branches:

$ git branch -vv
* develop 8821236 [origin/develop] refactor: update server port
  main    3975fbd [origin/main: ahead 1] refactor: update server port

Untracking upstream branches

To tell Git to stop tracking an upstream branch, you can checkout the desired local branch and use the git branch command as follows:

$ git branch --unset-upstream

Removing local upstream branches

To remove an upstream branch from the local repository, you can use the git branch command as follows:

$ git branch -r -d <origin>/<remote>

Example

This command will delete the local copy of the upstream branch origin/develop:

$ git branch -d -r origin/develop
Deleted remote-tracking branch origin/develop (was fb48a56).

Which is both confirmed by the branch being marked as "gone":

$ git branch -vv
* develop fb48a56 [origin/develop: gone] feat: add new hello endpoint
  main    4be366e [origin/main] feat: initial commit

And its removal from the list of remote branches:

$ git branch -r
  origin/main

Removing remote upstream branches

To remove a branch from the remote repository, you can use the git push command as follows:

$ git push <origin> -delete <remote>

⚠️ Warning: This operation is irreversible! Before deleting an upstream branch from the repository, first make sure that the branch has been fully merged and that all your collaborators have been warned!

Example

This command will delete the origin/develop branch from the remote repository:

$ git push origin --delete develop
To github.com:learnbackend/test.git
 - [deleted]         develop

Summary

Here's a summary of what you've learned in this lesson:

  • Pushing means uploading the changes made in a local repository to a remote repository.
  • Pulling means downloading and integrating the changes made to a remote repository into a local repository.
  • Fetching means downloading the changes made to a remote repository without integrating them.
  • A local branch is a branch that only exists on your local copy of the Git repository.
  • A remote branch is a branch that exists on the remote repository.
  • An upstream branch is a remote branch that is tracked by a local branch.
  • A local branch is ahead if it has more commits than its upstream branch.
  • A local branch is behind if it has less commits than its upstream branch.
  • The git branch -r command is used to list the remote branches present in the local repository.
  • The git branch -vv command is used to list the local branches and their upstream branches.
  • The git push command is used to upload local commits to a remote branch.
  • The git pull command is used to download and merge commits from a remote branch.
  • The git fetch command is used to download branches and commits from the remote repository without merging them.
  • The git switch -c command is used to create a local branch from a remote branch.
  • The git branch --unset-upstream command is used to track a remote branch.
  • The git branch -r -d command is used to remove a remote branch from the local repository.
  • The git push --delete command is used to remove a remote branch from the remote repository.

Enjoying the courses?

I've made these courses completely free so anyone can learn from them. If they've helped you and you'd like to actively support the work behind BackendBrewery, you can leave a tip:

Support BackendBrewery
Pushing & Pulling Commits in Git | Backend Brewery