Git: Working with Remotes

A remote is a reference to another copy of the repository — usually on a server (GitHub, GitLab, a VPS). Remotes let multiple people share work. Git keeps local "remote-tracking branches" (like origin/main) as snapshots of the last known state of the remote; these are updated with git fetch.

Commands Covered

git remote · git fetch · git pull · git push · git ls-remote

git remote

# List remotes
$ git remote
# origin

# List remotes with URLs
$ git remote -v
# origin  https://github.com/user/repo.git (fetch)
# origin  https://github.com/user/repo.git (push)

# Add a remote
$ git remote add origin https://github.com/user/repo.git
$ git remote add upstream https://github.com/original/repo.git

# Remove a remote
$ git remote remove upstream
$ git remote rm upstream   # same

# Rename a remote
$ git remote rename origin old-origin

# Change a remote's URL
$ git remote set-url origin https://github.com/user/new-repo.git
# Switch from HTTPS to SSH:
$ git remote set-url origin git@github.com:user/repo.git

# Show detailed info about a remote (branches, tracking info)
$ git remote show origin

# Prune stale remote-tracking branches (branches deleted on remote)
$ git remote prune origin
$ git fetch --prune   # same effect; prune during fetch

git fetch

Downloads objects and refs from a remote but does NOT modify your working tree or local branches. Safe to run at any time — it just updates your remote-tracking branches (origin/main, etc.).

# Fetch all changes from origin (the default remote)
$ git fetch
$ git fetch origin

# Fetch a specific branch
$ git fetch origin main
$ git fetch origin feature-login

# Fetch from all remotes
$ git fetch --all

# Fetch and prune stale remote-tracking branches
$ git fetch --prune
$ git fetch -p

# Fetch all tags
$ git fetch --tags

# Fetch and show what changed
$ git fetch origin
$ git log HEAD..origin/main    # commits on remote not yet local
$ git diff HEAD origin/main    # diff between local and remote

git pull

git pull is git fetch followed by a merge (or rebase) in one step. It integrates remote changes into your current branch.

# Pull from the tracked remote branch
$ git pull

# Pull from a specific remote and branch
$ git pull origin main

# Pull with rebase instead of merge (cleaner history)
$ git pull --rebase
$ git pull --rebase origin main

# Set rebase as the default pull strategy globally:
$ git config --global pull.rebase true

# Pull and prune stale remote-tracking branches
$ git pull --prune

# Pull without merging or rebasing — just fetch (then you decide)
$ git fetch origin
$ git log HEAD..origin/main    # review incoming commits
$ git merge origin/main        # then manually merge

# If a pull creates merge conflicts:
$ git pull
# CONFLICT — fix files
$ git add conflicted-file.c
$ git commit   # or: git rebase --continue if pulling with --rebase

pull vs. fetch + merge

CommandWhat it doesWhen to use
git pullfetch + merge in one stepQuick; fine when you trust what's on the remote
git pull --rebasefetch + rebase in one stepCleaner linear history; preferred in many teams
git fetch then reviewfetch without integratingWhen you want to see what changed before merging

git push

Uploads local commits to a remote repository. Push only works cleanly when the remote doesn't have commits you don't have locally (i.e., your branch is up to date with the remote).

# Push current branch to its tracked remote branch
$ git push

# Push to a specific remote and branch
$ git push origin main
$ git push origin feature-login

# Push and set the upstream tracking branch (first push of a new branch)
$ git push -u origin feature-login
$ git push --set-upstream origin feature-login
# After -u, you can just use: git push (no args needed)

# Push all local branches
$ git push --all origin

# Push all tags
$ git push --tags
# Push a specific tag
$ git push origin v1.0.0

# Delete a remote branch
$ git push origin --delete feature-old
$ git push origin :feature-old   # older syntax — same effect

# Delete a remote tag
$ git push origin --delete v0.1-beta
$ git push origin :refs/tags/v0.1-beta

# Force push (rewrite remote history — use with extreme caution)
$ git push --force
# Safer alternative: fails if someone else has pushed since your last fetch
$ git push --force-with-lease

When Push Fails

# Push rejected because remote has commits you don't:
$ git push
# error: failed to push some refs to 'origin'
# hint: Updates were rejected because the remote contains work that you do not have locally.

# Fix: fetch and integrate, then push
$ git fetch origin
$ git rebase origin/main   # or: git merge origin/main
$ git push

git ls-remote

Lists references (branches, tags) in a remote repository without cloning or fetching it.

# List all refs on a remote
$ git ls-remote origin

# List only branches (heads)
$ git ls-remote --heads origin

# List only tags
$ git ls-remote --tags origin

# Filter by pattern
$ git ls-remote origin 'refs/heads/feature-*'

# Check if a specific branch exists on remote
$ git ls-remote --heads origin feature-login
# Empty output = branch doesn't exist on remote

# Use with any URL (without adding a remote)
$ git ls-remote https://github.com/user/repo.git

Tracking Branches

# See which local branches track which remote branches
$ git branch -vv
# main  abc1234 [origin/main] Latest commit message
# feat  def5678 [origin/feat: ahead 2] More commits

# "ahead N" = N local commits not yet pushed
# "behind N" = N remote commits not yet fetched/merged
# "ahead N, behind M" = diverged — both have new commits

# Set up tracking for an existing local branch
$ git branch -u origin/main main
$ git branch --set-upstream-to=origin/develop develop

References