Check if pull needed in Git
How do I check whether the remote repository has changed and I need to pull?
Now I use this simple script:
git pull --dry-run | grep -q -v 'Already up-to-date.' && changed=1
But it is rather heavy.
Is there a better way? The ideal solution would check all the remote branches, and return names of the changed branches and the number of new commits in each one.
First use git remote update
, to bring your remote refs up to date. Then you can do one of several things, such as:
git status -uno
will tell you whether the branch you are tracking is ahead, behind or has diverged. If it says nothing, the local and remote are the same.git show-branch *master
will show you the commits in all of the branches whose names end in 'master' (eg master and origin/master).
If you use -v
with git remote update
(git remote -v update
) you can see which branches got updated, so you don't really need any further commands.
However, it looks like you want to do this in a script or program and end up with a true/false value. If so, there are ways to check the relationship between your current HEAD commit and the head of the branch you're tracking, although since there are four possible outcomes you can't reduce it to a yes/no answer. However, if you're prepared to do a pull --rebase
then you can treat "local is behind" and "local has diverged" as "need to pull", and the other two as "don't need to pull".
You can get the commit id of any ref using git rev-parse <ref>
, so you can do this for master and origin/master and compare them. If they're equal, the branches are the same. If they're unequal, you want to know which is ahead of the other. Using git merge-base master origin/master
will tell you the common ancestor of both branches, and if they haven't diverged this will be the same as one or the other. If you get three different ids, the branches have diverged.
To do this properly, eg in a script, you need to be able to refer to the current branch, and the remote branch it's tracking. The bash prompt-setting function in /etc/bash_completion.d
has some useful code for getting branch names. However, you probably don't actually need to get the names. Git has some neat shorthands for referring to branches and commits (as documented in git rev-parse --help
). In particular, you can use @
for the current branch (assuming you're not in a detached-head state) and @{u}
for its upstream branch (eg origin/master
). So git merge-base @ @{u}
will return the (hash of the) commit at which the current branch and its upstream diverge and git rev-parse @
and git rev-parse @{u}
will give you the hashes of the two tips. This can be summarized in the following script:
#!/bin/sh
UPSTREAM=${1:-'@{u}'}
LOCAL=$(git rev-parse @)
REMOTE=$(git rev-parse "$UPSTREAM")
BASE=$(git merge-base @ "$UPSTREAM")
if [ $LOCAL = $REMOTE ]; then
echo "Up-to-date"
elif [ $LOCAL = $BASE ]; then
echo "Need to pull"
elif [ $REMOTE = $BASE ]; then
echo "Need to push"
else
echo "Diverged"
fi
Note: older versions of git didn't allow @
on its own, so you may have to use @{0}
instead.
The line UPSTREAM=${1:-'@{u}'}
allows you optionally to pass an upstream branch explicitly, in case you want to check against a different remote branch than the one configured for the current branch. This would typically be of the form remotename/branchname. If no parameter is given, the value defaults to @{u}
.
The script assumes that you've done a git fetch
or git remote update
first, to bring the tracking branches up to date. I didn't build this into the script because it's more flexible to be able to do the fetching and the comparing as separate operations, for example if you want to compare without fetching because you already fetched recently.
If you have an upstream branch
git fetch <remote>
git status
If you don't have an upstream branch
Compare the two branches:
git fetch <remote>
git log <local_branch_name>..<remote_branch_name> --oneline
For example:
git fetch origin
# See if there are any incoming changes
git log HEAD..origin/master --oneline
(I'm assuming origin/master
is your remote tracking branch)
If any commits are listed in the output above, then you have incoming changes -- you need to merge. If no commits are listed by git log
then there is nothing to merge.
Note that this will work even if you are on a feature branch -- that does not have a tracking remote, since if explicitly refers to origin/master
instead of implicitly using the upstream branch remembered by Git.
If this is for a script, you can use:
git fetch
$(git rev-parse HEAD) == $(git rev-parse @{u})
(Note: the benefit of this vs. previous answers is that you don't need a separate command to get the current branch name. "HEAD" and "@{u}" (the current branch's upstream) take care of it. See "git rev-parse --help" for more details.)
The command
git ls-remote origin -h refs/heads/master
will list the current head on the remote -- you can compare it to a previous value or see if you have the SHA in your local repo.
Here's a Bash one-liner that compares the current branch's HEAD commit hash against its remote upstream branch, no heavy git fetch
or git pull --dry-run
operations required:
[ $(git rev-parse HEAD) = $(git ls-remote $(git rev-parse --abbrev-ref @{u} | \
sed 's/\// /g') | cut -f1) ] && echo up to date || echo not up to date
Here's how this somewhat dense line is broken down:
- Commands are grouped and nested using
$(x)
Bash command-substitution syntax. -
git rev-parse --abbrev-ref @{u}
returns an abbreviated upstream ref (e.g.origin/master
), which is then converted into space-separated fields by the pipedsed
command, e.g.origin master
. - This string is fed into
git ls-remote
which returns the head commit of the remote branch. This command will communicate with the remote repository. The pipedcut
command extracts just the first field (the commit hash), removing the tab-separated reference string. -
git rev-parse HEAD
returns the local commit hash. - The Bash syntax
[ a = b ] && x || y
completes the one-liner: this is a Bash string-comparison=
within a test construct[ test ]
, followed by and-list and or-list constructs&& true || false
.