758

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.

5
  • 16
    Please note: "git pull --dry-run" does not work as probably expected. It seems, that git pull passes unknown options directly to git fetch. The result is that of a normal git pull.
    – user1004858
    Commented Oct 20, 2011 at 9:21
  • 36
    "pull" is just a short way to do "fetch" and "merge" at once, if you need to check the remote repo status you are really simulating a "fetch". So git fetch -v --dry-run is what you need. Commented Mar 10, 2013 at 16:28
  • I tried the solution proposed by OP, and it does returned nothing. Probably not the best approach?
    – carloswm85
    Commented Aug 21, 2021 at 14:41
  • I don't understand if the original question was about a shell script (having something clean while writing a script) or having something simple during everyday shell usage. Can you specify it in the question? Commented Oct 19, 2022 at 15:37
  • 1
    You may be interested in Git's new feature: autostash.
    – Flimm
    Commented Oct 23, 2023 at 14:06

29 Answers 29

1039

First use git remote update, to bring your remote refs up to date. Then you can do one of several things, such as:

  1. 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.

  2. 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 ("local is ahead" and "same") 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.

25
  • 4
    @takeshin I guess you could combine git ls-remote origin -h refs/heads/master as suggested by @brool with git rev-list --max-count=1 origin/master. If they return the same hash, the remote branch hasn't changed since you last updated your remote refs (with pull, fetch, remote update, etc.) This would have the advantage that you wouldn't have to pull down the content of all the commits right away, but could leave that for a more convenient time. However, since remote update is non-destructive, you might as well do it anyway. Commented Jul 19, 2010 at 19:09
  • 3
    You could also try git status -s -u no, which gives a shorter output than git status -u no. Commented Jul 17, 2012 at 1:56
  • 2
    @mhulse, git remote -v update. Look at the output of git remote --help for a fuller explanation. Commented Dec 13, 2013 at 1:39
  • 1
    @ChrisMaes Good point. The more explicit syntax is needed with older versions of git. I experimented with the various systems I have and found that @{u} works with git 1.8.3.2 but @ doesn't. However @ works with 1.8.5.4. Moral of the story: git keeps improving and it's worth having the most recent version you can. Commented Apr 18, 2014 at 22:03
  • 1
    A specifier is now required for @. You can use @{0} instead of @.
    – Ben Davis
    Commented May 13, 2014 at 17:28
151

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.

9
  • 2
    Even a shorter notation git fetch; git log HEAD.. --oneline can be used if there's a default remote branch for local one. Commented Nov 4, 2012 at 21:20
  • @philpirozhkov If you have a default remote branch, a simple "git status" should do I think. My answer was a generic one for any two branches, where one may or may not be tracking the other.
    – Debajit
    Commented Nov 20, 2012 at 2:14
  • 68
    git rev-list HEAD...origin/master --count will give you the total number of "different" commits between the two. Commented Feb 5, 2013 at 19:23
  • 1
    short and simple. My favorite solution that just shows the new commits (thumbs up twice) Commented Mar 18, 2014 at 9:05
  • 1
    A side note of @JakeBerger comment, you first have to git fetch to receive the latest changes at remote. Commented Jan 12, 2022 at 17:13
109

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.)

5
  • I discovered @{u} independently and had updated my answer before I saw yours. Commented Mar 28, 2014 at 22:41
  • 1
    Will git rev-parse @{u} actually show the latest commit without a git fetch? Commented Apr 17, 2014 at 20:13
  • 7
    This was the ticket! Although, your logic is using == which means "if there are NO changes from upstream". I used != to check for "if there ARE changes from upstream" for my application. Don't forget to git fetch first!
    – ChrisPrime
    Commented Mar 20, 2015 at 5:26
  • 4
    I added git fetch, because it really is necessary to answer the original question. @ is short for HEAD btw. Commented Oct 16, 2015 at 18:35
  • 1
    Windows users will need single quotes around the @{u} e.g.git rev-parse '@{u}'
    – spuder
    Commented Jun 21, 2017 at 5:07
48

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 piped sed 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 piped cut 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.
3
  • 2
    I would not use /g on the sed if you use slashes in the branch names. That is "sed 's/\// /" only. Commented Jul 19, 2017 at 5:34
  • @wjordan Your solution fails when the remote repository isn't reachable (or under maintenance) and will trigger "up to date"
    – frame
    Commented Oct 16, 2019 at 8:19
  • @frame in almost all use-cases that would be preferable outcome. If the remote is currently unreachable, your local would semantically be up to date as of the last time you ran the script.
    – cowbert
    Commented Apr 17, 2023 at 18:11
44

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.

6
  • 1
    Any sample script to compare these values?
    – takeshin
    Commented Jul 19, 2010 at 8:12
  • 35
    git rev-list HEAD...origin/master --count will give you the total number of "different" commits between the two. Commented Feb 5, 2013 at 19:24
  • 3
    @jberger to clarify, that will only show the number of commits you're behind (not ahead and behind) and it only works if you git fetch or git remote update first. git status also shows a count, btw.
    – Dennis
    Commented Apr 29, 2013 at 4:26
  • 1
    @Dennis I thought .. is "commits in origin/master, subtracting HEAD" (i.e. number of commits behind). Whereas, ... is the symmetric difference (i.e. ahead and behind) Commented May 2, 2013 at 1:20
  • 4
    Excellent. As far as I can tell, this is the only solution that actually checks the origin for updates but doesn't implicitly do a fetch. Commented Apr 17, 2014 at 20:53
23

I suggest you go see the script https://github.com/badele/gitcheck. I have coded this script for check in one pass all your Git repositories, and it shows who has not committed and who has not pushed/pulled.

Here a sample result:

Enter image description here

3
  • 6
    neat, thinking about rewriting it in pure shell Commented Nov 7, 2013 at 19:35
  • 1
    Now, you can also use gitcheck directly from an docker container (with your files in your host) For more information see the gitcheck github project Commented May 6, 2015 at 19:28
  • A similar tool in bash git-multi-repo-tooling . git mrepo -c this will display all pending commits.
    – Greg
    Commented Feb 17, 2018 at 16:48
23

The below script works perfectly.

changed=0
git remote update && git status -uno | grep -q 'Your branch is behind' && changed=1
if [ $changed = 1 ]; then
    git pull
    echo "Updated successfully";
else
    echo "Up-to-date"
fi
1
  • Works on Ubuntu with .sh scripts and works with files changed (checks if pull is possible/needed instead of unclean state) Commented May 30, 2022 at 10:23
16

I just want to post this as an actual post as it is easy to miss this in the comments.

The correct and best answer for this question was given by @Jake Berger, Thank you very much dude, everyone need this and everyone misses this in the comments. So for everyone struggling with this here is the correct answer, just use the output of this command to know if you need to do a git pull. if the output is 0 then obviously there is nothing to update.

@stackoverflow, give this guy a bells. Thanks @ Jake Berger

# will give you the total number of "different" commits between the two
# Jake Berger Feb 5 '13 at 19:23
git rev-list HEAD...origin/master --count
3
  • Thanks for making it pretty Arnaud :) Commented Aug 13, 2021 at 7:54
  • 1
    Does this need a fetch to work as expected, or does the reference to origin/master mean git will query the remote without fetching anything locally? Commented Aug 25, 2021 at 15:03
  • @NickChammas this command is local, so it is useful after fetch and before pull/merge/reset
    – Akom
    Commented Aug 31, 2021 at 14:25
14

I think the best way to do this would be:

git diff remotes/origin/HEAD

Assuming that you have the this refspec registered. You should if you have cloned the repository, otherwise (i.e., if the repo was created de novo locally, and pushed to the remote), you need to add the refspec explicitly.

12

I based this solution on the comments of @jberger.

if git checkout master &&
    git fetch origin master &&
    [ `git rev-list HEAD...origin/master --count` != 0 ] &&
    git merge origin/master
then
    echo 'Updated!'
else
    echo 'Not updated.'
fi
1
  • referring to your previous comment, at this point in time I cannot give you a definite answer. At the time I made those comments, I was diving into the depths of git and particularly remotes and diffs. It has been a few months since then and a lot of that knowledge is buried inside my brain. ;) If you're looking for the number of 'different' commits between the two, then ... seems to be a valid part of your solution. Commented Jun 30, 2013 at 14:18
12

There are many very feature rich and ingenious answers already. To provide some contrast, I could make do with a very simple line.

# Check return value to see if there are incoming updates.
if ! git diff --quiet remotes/origin/HEAD; then
 # pull or whatever you want to do
fi
2
  • 2
    Original answer was lacking '!' in the if. The return value from git diff is zero, when there are no changes.
    – thuovila
    Commented Mar 3, 2016 at 9:00
  • 1
    IMO best solution out there, altough I need to substitute "remotes/origin/HEAD" with "origin/master" or other revision Commented Mar 26, 2020 at 18:29
7

If you run this script, it will test if the current branch need a git pull:

#!/bin/bash

git fetch -v --dry-run 2>&1 |
    grep -qE "\[up\s+to\s+date\]\s+$(
        git branch 2>/dev/null |
           sed -n '/^\*/s/^\* //p' |
                sed -r 's:(\+|\*|\$):\\\1:g'
    )\s+" || {
        echo >&2 "Current branch need a 'git pull' before commit"
        exit 1
}

It's very convenient to put it as a Git hook pre-commit to avoid

Merge branch 'foobar' of url:/path/to/git/foobar into foobar

when you commit before pulling.

To use this code as a hook, simply copy/paste the script in

.git/hooks/pre-commit

and

chmod +x .git/hooks/pre-commit
6

I would do the way suggested by brool. The following one-line script takes the SHA1 of your last commited version and compares it to the one of the remote origin, and pull changes only if they differ. And it's even more light-weight of the solutions based on git pull or git fetch.

[ `git log --pretty=%H ...refs/heads/master^` != `git ls-remote origin
-h refs/heads/master |cut -f1` ] && git pull
4
  • This command fails, if the git repository is cloned with "--depth 1" (to limit download size). Do you know, if there is a way to fix it? Commented Mar 8, 2014 at 9:53
  • The git log this is returning many lines, and giving a error "bash: [: too many arguments" I'd switch to git rev-parse --verify HEAD Commented Oct 30, 2014 at 17:45
  • 1
    This is a simple string comparison done by bash. If something fails I would suggest you to check your syntax (i.e. you're typing it wrong). First run git log --pretty=%H ...refs/heads/master^ to get the SHA1 of your last commited version, and then run git ls-remote origin -h refs/heads/master |cut -f1 to get the SHA1 of the remote origin. These two are git commands and have nothing to do with bash. What bash does inside the square brackets is to compare the output from the first command with the second one, and if they are equal it returns true and runs git pull. Commented Nov 7, 2014 at 16:07
  • "and if they are equal it returns true and runs git pull". I know I'm being nitpicky, but just to save someone confusion, that should be "and if they not equal". Also, for whatever reason, the first git command does not work for me. (I'm on git 2.4.1.) So I'm just using git log --pretty=%H master | head -n1 instead. But I'm not sure if that's exactly the same.
    – xd1le
    Commented May 20, 2015 at 15:10
6

Run git fetch (remote) to update your remote refs, it'll show you what's new. Then, when you checkout your local branch, it will show you whether it's behind upstream.

6
  • I think he already has the local branch checked out, so he needs something else to show whether it's behind etc. He can do this with git status. Commented Jul 19, 2010 at 5:15
  • True, after you've fetched remotes, git status will show that as well.
    – che
    Commented Jul 19, 2010 at 6:27
  • 1
    That's something in the mood git pull --dry-run does, but I think it is to heavy for a cron script run each minute.
    – takeshin
    Commented Jul 19, 2010 at 8:11
  • @takeshin: You can't check remote repositories without going on the network. If there isn't anything new fetch's not going to do much beyond than checking status. If you need a very fast and lightweight reaction on remote updates, you might want to look into hooking some kind of notifications to the remote repository.
    – che
    Commented Jul 19, 2010 at 22:12
  • @takeshin: if you're wanting to check the remote repo every minute I think you've missed the point of DVCS. The whole idea is to be able to develop independently for a while, and then put it all together smoothly later. It's not like cvs, svn, p4 etc. where you always have to be working on top of whatever is the latest in the repository. If you really need something that somebody else is working on, then you should use a different communication mechanism, such as email, to tell you when it's ready to pull. Commented Jul 20, 2010 at 1:38
6

All such complex sugestions while the solution is so short and easy:

#!/bin/bash

BRANCH="<your branch name>"
LAST_UPDATE=`git show --no-notes --format=format:"%H" $BRANCH | head -n 1`
LAST_COMMIT=`git show --no-notes --format=format:"%H" origin/$BRANCH | head -n 1`

git remote update
if [ $LAST_COMMIT != $LAST_UPDATE ]; then
        echo "Updating your branch $BRANCH"
        git pull --no-edit
else
        echo "No updates available"
fi
3
  • LAST_COMMIT and LAST_UPDATE are always equal even if there are changes
    – canbax
    Commented Nov 8, 2019 at 7:39
  • 1
    This solution is good and simple, put needs to have git remote update executed before your code, to get latest origin commit info
    – ak93
    Commented Nov 14, 2019 at 22:43
  • Should git remote update not append before git show commands ?
    – Setop
    Commented Nov 25, 2019 at 15:01
2

Here's my version of a Bash script that checks all repositories in a predefined folder:

https://gist.github.com/henryiii/5841984

It can differentiate between common situations, like pull needed and push needed, and it is multithreaded, so the fetch happens all at once. It has several commands, like pull and status.

Put a symlink (or the script) in a folder in your path, then it works as git all status (, etc.). It only supports origin/master, but it can be edited or combined with another method.

2

This one-liner works for me in zsh (from @Stephen Haberman's answer)

git fetch; [ $(git rev-parse HEAD) = $(git rev-parse @{u}) ] \
    && echo "Up to date" || echo "Not up to date"
1
git ls-remote | cut -f1 | git cat-file --batch-check >&-

will list everything referenced in any remote that isn't in your repo. To catch remote ref changes to things you already had (e.g. resets to previous commits) takes a little more:

git pack-refs --all
mine=`mktemp`
sed '/^#/d;/^^/{G;s/.\(.*\)\n.* \(.*\)/\1 \2^{}/;};h' .git/packed-refs | sort -k2 >$mine
for r in `git remote`; do 
    echo Checking $r ...
    git ls-remote $r | sort -k2 | diff -b - $mine | grep ^\<
done
1

Maybe this, if you want to add task as crontab:

#!/bin/bash
dir="/path/to/root"
lock=/tmp/update.lock
msglog="/var/log/update.log"

log()
{
        echo "$(date) ${1:-missing}" >> $msglog
}

if [ -f $lock ]; then
        log "Already run, exiting..."
else
        > $lock
        git -C ~/$dir remote update &> /dev/null
        checkgit=`git -C ~/$dir status`
        if [[ ! "$checkgit" =~ "Your branch is up-to-date" ]]; then
                log "-------------- Update ---------------"
                git -C ~/$dir pull &>> $msglog
                log "-------------------------------------"
        fi
        rm $lock

fi
exit 0
1

I use a version of a script based on Stephen Haberman's answer:

if [ -n "$1" ]; then
    gitbin="git -C $1"
else
    gitbin="git"
fi

# Fetches from all the remotes, although --all can be replaced with origin
$gitbin fetch --all
if [ $($gitbin rev-parse HEAD) != $($gitbin rev-parse @{u}) ]; then
    $gitbin rebase @{u} --preserve-merges
fi

Assuming this script is called git-fetch-and-rebase, it can be invoked with an optional argument directory name of the local Git repository to perform operation on. If the script is called with no arguments, it assumes the current directory to be part of the Git repository.

Examples:

# Operates on /abc/def/my-git-repo-dir
git-fetch-and-rebase /abc/def/my-git-repo-dir

# Operates on the Git repository which the current working directory is part of
git-fetch-and-rebase

It is available here as well.

1

Because Neils answer helped me so much here is a Python translation with no dependencies:

import os
import logging
import subprocess

def check_for_updates(directory:str) -> None:
    """Check git repo state in respect to remote"""
    git_cmd = lambda cmd: subprocess.run(
        ["git"] + cmd,
        cwd=directory,
        stdout=subprocess.PIPE,
        check=True,
        universal_newlines=True).stdout.rstrip("\n")

    origin = git_cmd(["config", "--get", "remote.origin.url"])
    logging.debug("Git repo origin: %r", origin)
    for line in git_cmd(["fetch"]):
        logging.debug(line)
    local_sha = git_cmd(["rev-parse", "@"])
    remote_sha = git_cmd(["rev-parse", "@{u}"])
    base_sha = git_cmd(["merge-base", "@", "@{u}"])
    if local_sha == remote_sha:
        logging.info("Repo is up to date")
    elif local_sha == base_sha:
        logging.info("You need to pull")
    elif remote_sha == base_sha:
        logging.info("You need to push")
    else:
        logging.info("Diverged")

check_for_updates(os.path.dirname(__file__))

hth

1
git ls-remote origin -h refs/heads/master

given by brool is the lightest way to just check if something at all has changed in the remote.

Starting from the local head:

$ git log -1 --oneline @
9e1ff307c779 (HEAD -> master, tag: v5.15-rc4, origin/master, origin/HEAD) Linux 5.15-rc4

I see that my pulled origin is up to date at that tag. git status says so too. But that is only the local up to date, the (fast-forward) merge after a fetch.

To check if the remote HEAD went somewhere, and also master, and maybe some new tags:

$ git ls-remote origin HEAD master --tags 'v5.1[56]-rc[345]*'

84b3e42564accd94c2680e3ba42717c32c8b5fc4        HEAD
84b3e42564accd94c2680e3ba42717c32c8b5fc4        refs/heads/master
71a6dc2a869beafceef1ce46a9ebefd52288f1d7        refs/tags/v5.15-rc3
5816b3e6577eaa676ceb00a848f0fd65fe2adc29        refs/tags/v5.15-rc3^{}
f3cee05630e772378957a74a209aad059714cbd2        refs/tags/v5.15-rc4
9e1ff307c779ce1f0f810c7ecce3d95bbae40896        refs/tags/v5.15-rc4^{}

HEAD is still on the same branch, but not the same commit anymore. That local @ commit stays with the v5.15-rc4 tag. This is about the same info as on top of the summary sheet on kernel.org git:

Branch: master <commit message> <author> age: 2 hours

Only that ls-remote collects less information - but then again only me I know that I am on 9e1ff... aka v5.15-rc4.

Instead of naming refs (HEAD, master) or tags one can also get a list of heads or branches from any repo:

$ git ls-remote --heads git://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 

af06aff2a0007976513134dfe993d55992dd866a        refs/heads/akpm
20bcee8e95f783c11a7bea1b6a40c70a37873e0a        refs/heads/akpm-base
a25006a77348ba06c7bc96520d331cd9dd370715        refs/heads/master
4d5a088c93cea1c821d02a2217c592391d9682e2        refs/heads/pending-fixes
4de593fb965fc2bd11a0b767e0c65ff43540a6e4        refs/heads/stable

Here a URL replaces "origin".


How do I check whether the remote repository has changed and I need to pull?

If you ask like that, just pull.

How do I check whether the remote repository has finally done something and I want to pull?

Then you fetch and check and merge.


With single git commands:

$ git rev-list -1  master
9e1ff307c779ce1f0f810c7ecce3d95bbae40896
$ git rev-list -1  @
9e1ff307c779ce1f0f810c7ecce3d95bbae40896

This by itself does not say much, but suppose I know I did not commit anything, then:

$ git ls-remote origin HEAD master
60a9483534ed0d99090a2ee1d4bb0b8179195f51        HEAD
60a9483534ed0d99090a2ee1d4bb0b8179195f51        refs/heads/master

Will tell me that the remote has changed. It really has since last edit. kernel.org says Age: 46 min. about this last commit on master.

After git fetch:

$ git rev-list -1 master     
9e1ff307c779ce1f0f810c7ecce3d95bbae40896

$ git rev-list -1 FETCH_HEAD
60a9483534ed0d99090a2ee1d4bb0b8179195f51

$ git log --oneline ..FETCH_HEAD        
60a9483534ed (origin/master, origin/HEAD) Merge tag 'warning-fixes-20211005' of git://git.kernel.org/pub/scm/linux/kernel/git/dhowells/linux-fs
f6274b06e326 Merge tag 'linux-kselftest-fixes-5.15-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/shuah/linux-kselftest
ef31499a87cf fscache: Remove an unused static variable
d9e3f82279bf fscache: Fix some kerneldoc warnings shown up by W=1
bc868036569e 9p: Fix a bunch of kerneldoc warnings shown up by W=1
dcb442b13364 afs: Fix kerneldoc warning shown up by W=1
c0b27c486970 nfs: Fix kerneldoc warning shown up by W=1
84b3e42564ac Merge tag 'media/v5.15-3' of git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media
b60be028fc1a Merge tag 'ovl-fixes-5.15-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/mszeredi/vfs
df5c18838ea8 Merge tag 'mips-fixes_5.15_1' of git://git.kernel.org/pub/scm/linux/kernel/git/mips/linux
206704a1fe0b media: atomisp: restore missing 'return' statement
740da9d7ca4e MIPS: Revert "add support for buggy MT7621S core detection"
1dc1eed46f9f ovl: fix IOCB_DIRECT if underlying fs doesn't support direct IO
2f9602870886 selftests: drivers/dma-buf: Fix implicit declaration warns
a295aef603e1 ovl: fix missing negative dentry check in ovl_rename()

Now I have all the information locally, but have not merged (yet). I also have all the objects downloaded. git show HASH or git diff HASH work.

In this case the merge is almost a no-op: fast-forward to the last commit and no extra (real) merge, let alone conflicts. This can be made sure by --ff-only:

$ git merge --ff-only FETCH_HEAD
Updating 9e1ff307c779..60a9483534ed
Fast-forward
...
... 

So how can I know when to pull? As soon as these two hashes are/will, be different: Updating 9e1ff307c779..60a9483534ed Fast-forward. They can not be the same, that would be "nothing to update".

The newest reflog commits say the same:

$ git log -10 --oneline -g
60a9483534ed (HEAD -> master, origin/master, origin/HEAD) HEAD@{0}: merge 60a9483534ed0d99090a2ee1d4bb0b8179195f51: Fast-forward
9e1ff307c779 (tag: v5.15-rc4) HEAD@{1}: pull: Fast-forward

Looking at this, a new tag appearing is maybe the best trigger and also target in this case; that leads back to the git ls-remote origin --tags PATTERN.


...and don't tell me git remote show is another method:

show Gives some information about the remote .

With -n option, the remote heads are not queried first with git ls-remote ; cached information is used instead.

1

To automate git pull on a desired branch:
Use like: ./pull.sh "origin/main"

pull.sh

#!/bin/bash

UPSTREAM=${1:-'@{u}'}
DIFFCOMM=$(git fetch origin --quiet; git rev-list HEAD..."$UPSTREAM" --count)
if [ "$DIFFCOMM" -gt 0 ]; then
  echo "Pulling $UPSTREAM";
  git pull;
else
  echo "Up-to-date";
fi

0

After reading many answers and multiple posts, and spending half a day trying various permutations, this is what I have come up with.

If you are on Windows, you may run this script in Windows using Git Bash provided by Git for Windows (installation or portable).

This script requires arguments

- local path e.g. /d/source/project1
- Git URL e.g. https://[email protected]/username/project1.git
- password

if a password should not be entered on the command line in plain text,
then modify the script to check if GITPASS is empty; do not
replace and let Git prompt for a password

The script will

- Find the current branch
- Get the SHA1 of the remote on that branch
- Get the SHA1 of the local on that branch
- Compare them.

If there is a change as printed by the script, then you may proceed to fetch or pull. The script may not be efficient, but it gets the job done for me.

Update - 2015-10-30: stderr to dev null to prevent printing the URL with the password to the console.

#!/bin/bash

# Shell script to check if a Git pull is required.

LOCALPATH=$1
GITURL=$2
GITPASS=$3

cd $LOCALPATH
BRANCH="$(git rev-parse --abbrev-ref HEAD)"

echo
echo git url = $GITURL
echo branch = $BRANCH

# Bash replace - replace @ with :password@ in the GIT URL
GITURL2="${GITURL/@/:$GITPASS@}"
FOO="$(git ls-remote $GITURL2 -h $BRANCH 2> /dev/null)"
if [ "$?" != "0" ]; then
  echo cannot get remote status
  exit 2
fi
FOO_ARRAY=($FOO)
BAR=${FOO_ARRAY[0]}
echo [$BAR]

LOCALBAR="$(git rev-parse HEAD)"
echo [$LOCALBAR]
echo

if [ "$BAR" == "$LOCALBAR" ]; then
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  echo No changes
  exit 0
else
  #read -t10 -n1 -r -p 'Press any key in the next ten seconds...' key
  #echo pressed $key
  echo There are changes between local and remote repositories.
  exit 1
fi
0

For the windows users who end up on this question looking for this, I've modified some of the answer into a powershell script. Tweak as necessary, save to a .ps1 file and run on demand or scheduled if you like.

cd C:\<path to repo>
git remote update                           #update remote
$msg = git remote show origin               #capture status
$update = $msg -like '*local out of date*'
if($update.length -gt 0){                   #if local needs update
    Write-Host ('needs update')
    git pull
    git reset --hard origin/master
    Write-Host ('local updated')
} else {
    Write-Host ('no update needed')
}
0

My solution, using git commands and simple string comparison:

BRANCH="develop"
git remote update

# Get the last 20 commits on the local branch
LOCAL_LOG=`git log --oneline -n20`

# Get the last commit on upstream
LAST_REV=`git rev-parse --short upstream/${BRANCH}`

# Check if the commit from upstream appears on the local commits    
if [[ ${LOCAL_LOG} =~ ${LAST_REV} ]]; then
  echo "[INFO] Local branch is up to date"
else
  echo "[WARNING] Local branch is out of date. Last revision ${LAST_REV}"
  git pull origin ${BRANCH} --no-edit
  git pull upstream ${BRANCH} --no-edit
fi
0

I made a Windows Batch file to do this exact thing. I had a Ren'Py game where i want to check for a pull, then if there is one pull it and delete the cache (.rpyc) files, then run the app. You can adapt it for your own repo by removing the .rpyc and .exe steps.

@echo off
setlocal

set file="gitresult.log"
set maxbytesize=1

echo Checking for updates
git pull --dry-run > %file% 2>&1

rem check size
FOR /F "usebackq" %%A IN ('%file%') DO set size=%%~zA

if %size% LSS %maxbytesize% (
    rem 0 bytes, empty file, up to date
    goto :end
) ELSE (
    rem 1+ bytes, there are changes
    goto :doupdate
)
exit /b

:doupdate
echo Grabbing updates
git pull
echo Deleting .rpyc files
del /Q /S /F *.rpyc

:end
ECHO Local repo is up to date
echo Operation complete, starting LR2
start gameExecutable.exe
exit
-1

Using simple regexp:

str=$(git status) 
if [[ $str =~ .*Your\ branch\ is\ behind.*by.*commits,\ and\ can\ be\ fast-forwarded ]]; then
    echo `date "+%Y-%m-%d %H:%M:%S"` "Needs pull"
else
    echo "Code is up to date"
fi
1
  • 2
    This will not work. git status is only a local check, and so it would only tell you if your branch is behind if you've already updated your remote defs.
    – minhaz1
    Commented Mar 16, 2018 at 18:54
-5

You can also find a Phing script who does that now.

I needed a solution to update my production environments automatically and we're very happy thanks to this script that I'm sharing.

The script is written in XML and needs Phing.

0

Not the answer you're looking for? Browse other questions tagged or ask your own question.