Home / Alt manpages / git-pull(1)

  • git-pull(1)
  • User command
  • linux

Update a Branch Safely with git pull

A blind git pull can turn a five-second update into a merge commit you never asked for. This walkthrough shows how to check the branch first, choose fast-forward, merge or rebase on purpose, and get out cleanly if the histories have diverged. The examples use Git 2.43.0 and the installed git-pull(1) manual. Allow 10 to 15 minutes for a routine pull, longer if you hit a conflict.

  • You need: a cloned repository, a network connection to its remote, and a working branch with an upstream configured.
  • No sudo: these are ordinary user commands. Elevated privileges can leave root-owned files in your repository, which is a mess for later.

1. Check the repository before pulling

Change to the repository and look at the current branch, upstream and working tree:

$ cd /path/to/repository
$ git status --short --branch
## main...origin/main
$ git remote -v
origin  [email protected]:EXAMPLE/PROJECT.git (fetch)
origin  [email protected]:EXAMPLE/PROJECT.git (push)
  • Read the first line. It should name the branch you mean to update.
  • A clean status has nothing below it. Modified, deleted or untracked files mean stop and decide what to do with them first.
  • Commit or stash deliberately. Use git stash push -u -m "before pull", not a blind pull, to hide unfinished work.

Checkpoint

Run git branch -vv. It shows the upstream selected for each local branch. With no upstream set, an argument-less git pull has nothing to integrate against.

2. Preview the fetch without changing the branch

Fetch is the first half of pull. Run it with the dry-run flag through git pull to check the remote contact and see what would come down, without touching your branch:

$ git pull --dry-run
From github.com:EXAMPLE/PROJECT
   1a2b3c4..5d6e7f8  main       -> origin/main

Exact output varies by server and by whether anything changed. --dry-run still talks to the remote, it just does not update your branch. If the remote is unreachable, fix the network, authentication or URL problem first: check the configured URL with git remote get-url origin.

3. Choose the integration policy on purpose

Git will not guess how you want history to look, so tell it.

  • Branch only behind upstream: fast-forward only. This moves your branch pointer and creates no new commit.
$ git pull --ff-only
Updating 1a2b3c4..5d6e7f8
Fast-forward
 src/report.txt | 4 ++++
 1 file changed, 4 insertions(+)

On Git 2.43.0, --ff-only is also the default when no other reconciliation method is configured. It fails rather than inventing a merge when both sides have new commits, which is exactly what you want from a default.

  • Project uses merge commits deliberately: say so.
$ git pull --no-rebase
Merge made by the 'ort' strategy.
  • Project keeps history linear: rebase instead.
$ git pull --rebase
Successfully rebased and updated refs/heads/main.

Rebase rewrites the IDs of your local commits. Do not use it casually on commits already shared with other people. A repository can record a preference through pull.rebase or pull.ff, but check local configuration before trusting an unfamiliar checkout:

$ git config --show-origin --get-regexp '^(pull\.|branch\..*\.(remote|merge|rebase))$'

A flag on the command line only applies to this one pull, which makes it easier to reason about than a repository setting you are not sure of.

4. Pull a named remote branch when needed

An ordinary pull uses the current branch's upstream. To integrate a particular branch explicitly, name the remote and refspec:

$ git pull --ff-only origin main

The refspec picks what to fetch and integrate into the current branch, it does not switch branches first. Confirm your destination with git branch --show-current: pulling origin/main while checked out on a feature branch updates that feature branch, not main.

5. Pause before a merge commits itself

Pull changes both history and files. Before accepting a merge, you can stop just short of the merge commit:

$ git pull --no-rebase --no-commit
Automatic merge went well; stopped before committing as requested

This pause does not affect a fast-forward, which has no merge commit to delay in the first place. Use --no-ff --no-commit when you specifically want a review point before the commit lands. Inspect with git diff --cached, then run git commit, or back out with git merge --abort if Git reports a merge in progress.

Tip

For a one-off pull with local changes, --autostash stashes them and reapplies afterwards. Treat it as a convenience, not a guarantee the reapply will be clean; stashing and inspecting the work yourself is the safer route.

6. Recover when histories conflict

A fast-forward-only pull that fails with a "branches have diverged" message is a decision point, not an invitation to bolt on a random flag. Look at both sides first:

$ git log --oneline --graph --decorate --all -20
$ git status
  • Project wants a merge: rerun with --no-rebase.
  • Project wants linear history and your commits are not shared: rerun with --rebase.
  • Merge conflict you do not want to resolve now: run git merge --abort.
  • Rebase conflict you do not want to resolve now: run git rebase --abort.
$ git merge --abort
$ git rebase --abort

Both return the operation to its pre-operation state as far as Git can manage. Check git status afterwards. Warning: do not reach for git reset --hard as a first response, it discards uncommitted changes and is hard to undo.

7. Verify the result

After a successful pull, check the branch and recent history rather than trusting a quiet terminal:

$ git status --short --branch
## main...origin/main
$ git log -1 --oneline
5d6e7f8 Update report parser
$ git rev-list --left-right --count HEAD...@{upstream}
0       0

That last command should report zero commits unique to either side once the upstream is the branch you intended. Run the project's tests or build before you publish anything further: a successful pull proves Git integrated objects, not that the application still works.

Done means

  • Checked first. Branch, upstream and working tree were clean before fetching.
  • Chose deliberately. You picked --ff-only, --no-rebase or --rebase rather than accepting a default you had not thought about.
  • Local edits accounted for. Committed, stashed or intentionally left alone.
  • Conflicts closed out. Resolved, or aborted with the matching Git command.
  • Verified, not assumed. Status, log and upstream count all confirm the result you meant.