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.
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.
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
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
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.
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
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
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:
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
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.)
git fetch <remote>
git status
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.
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.
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
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
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"
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
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.
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
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.
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
git rev-list HEAD...origin/master --count will give you the total number of "different" commits between the two. – Jake Berger Feb 5 '13 at 19:23
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')
}
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.
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
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:
$(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
.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.[ 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
.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
Source: Stackoverflow.com