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.
The route
Jump straight to the step you need, or tick off Done means at the end.
- 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-rebaseor--rebaserather 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.