How can I check in a Bash script if my local Git repository has changes?
GitGit Problem Overview
There are some scripts that do not work correctly if they check for changes.
I tried it like this:
VN=$(git describe --abbrev=7 HEAD 2>/dev/null)
git update-index -q --refresh
CHANGED=$(git diff-index --name-only HEAD --)
if [ ! -z $CHANGED ];
then VN="$VN-mod"
fi
Is there some kind of boolean check if there has been changes since the last commit, or how can I really test if there are new changes to my local repository?
I'm doing all this for a version creation script (that I found somewhere here).
Git Solutions
Solution 1 - Git
Using git status
:
cd /git/directory
if [[ `git status --porcelain` ]]; then
# Changes
else
# No changes
fi
Solution 2 - Git
What you're doing will almost work: you should quote $CHANGED
in case it's empty, and -z
tests for empty, which means no changes. What you meant was:
if [ -n "$CHANGED" ]; then
VN="$VN-mod"
fi
A quote from Git's GIT-VERSION-GEN
:
git update-index -q --refresh
test -z "$(git diff-index --name-only HEAD --)" ||
VN="$VN-dirty"
It looks like you were copying that, but you just forgot that detail of quoting.
Of course, you could also just do this:
if git diff-index --quiet HEAD --; then
# No changes
else
# Changes
fi
Or if you only care about the "something has changed" case:
if ! git diff-index --quiet HEAD --; then
VN="$VN-mod"
fi
Using --quiet
has the benefit that Git can stop processing as soon as it encounters a single diff, so it may not have to check your entire work tree.
Solution 3 - Git
Although Jefromi's answer is good, I'm posting this just for reference.
From the Git source code there is a sh
script which includes the following.
require_clean_work_tree () {
git rev-parse --verify HEAD >/dev/null || exit 1
git update-index -q --ignore-submodules --refresh
err=0
if ! git diff-files --quiet --ignore-submodules
then
echo >&2 "Cannot $1: You have unstaged changes."
err=1
fi
if ! git diff-index --cached --quiet --ignore-submodules HEAD --
then
if [ $err = 0 ]
then
echo >&2 "Cannot $1: Your index contains uncommitted changes."
else
echo >&2 "Additionally, your index contains uncommitted changes."
fi
err=1
fi
if [ $err = 1 ]
then
test -n "$2" && echo >&2 "$2"
exit 1
fi
}
Solution 4 - Git
This works too:
if [ $(git status --porcelain | wc -l) -eq "0" ]; then
echo " 🟢 Git repo is clean."
else
echo " 🔴 Git repo dirty. Quit."
exit 1
fi
Solution 5 - Git
I had a similar problem, but I had to check for added files also. So I did the following:
cd /local/repo
RUN=0
git diff --no-ext-diff --quiet --exit-code || RUN=1
if [ $RUN = 0 ]; then
RUN=`git ls-files --exclude-standard --others| wc -l`
fi
if [ $RUN = 0 ]; then
exit 0
fi
Solution 6 - Git
git status
is your friend
Change to the Git directory for git status
to work:
cd c:/path/to/.git
Set a variable to set the work tree so you don't get 'This operation must be run in a work tree' error:
WORKTREE=c:/path/to/worktree
Capture the git status
output in a Bash variable
Use --porcelain
which guarantees to be in a standard format and parsable:
CHANGED=$(git --work-tree=${WORKTREE} status --porcelain)
If -n (not null), we have changes.
if [ -n "${CHANGED}" ]; then
echo 'changed';
else
echo 'not changed';
fi
Solution 7 - Git
my 2 cents:
git status --porcelain | head -1
returns only one line instead of a long list
Solution 8 - Git
The OP's question is more than 9 years old now. I don't know what man git-status
said then, but here's what it says now:
--porcelain[=<version>]
Give the output in an easy-to-parse format for scripts. This is similar to the
short output, but will remain stable across Git versions and regardless of user
configuration. See below for details.
The version parameter is used to specify the format version. This is optional and
defaults to the original version v1 format.
This suggests that the --porcelain
argument is well-suited to testing the status of a repo for changes.
Wrt the OP's question, "Is there some kind of boolean check if there has been changes since the last commit, or how can I really test if there are new changes to my local repository?"
I don't think that bash
has boolean data types per se, but this may be close enough:
[ -z "`git status --porcelain`" ] && echo "NULL-NO DIFFS" || echo "DIFFS EXIST"
This can be re-cast as a if-then-else
form for a script, or executed as-is fm the CLI while in the git repo folder. Otherwise, use the -C
option with a path spec to the repo of interest:
git -C ~/path/to/MyGitRepo status --porcelain
Addendum:
- Some advise using the
-u, --untracked-file
option to avoid reporting status on files one wishes to ignore. Note this comes with an unfortunate side-effect: files that are newly added are not statused either. The option is useful in some situations, but consider it carefully before using.
Solution 9 - Git
I've looked up dozens of Stack Overflow answers and none of them seemed to suffice my expectations. That is, the script should error if:
- there are any differences between the current local state and the
origin/master
(including: changes to existing files; new uncommitted and committed files, and file deletions), or master
has commits that haven't been pushed yet toorigin/master
.
The final script can be used like this:
if ! (git diff --exit-code origin/master..master > /dev/null) \
|| ! (git diff --exit-code master > /dev/null) \
|| ! [[ -z "$(git status --porcelain)" ]] ; then
echo "Your local repo has some changes that aren't pushed to origin/master ."
exit 1
fi
Explanation:
git status --porcelain
will render list of files that aren't committed yet. Wrapping this with[[ -z "$(...)" ]]
makes it error if the command has returned a non-empty string.git diff --exit-code master > /dev/null
will error if your current committed repo state differs frommaster
branch.git diff --exit-code origin/master..master > /dev/null
will error if your currentmaster
branch differs fromorigin/master
.
This is useful if e.g. you want to make sure your repo is probably safe to be deleted/cleared up.
Why is that better?
- answers that rely only on
git diff --exit-code ...
will not error if you have new uncommitted non-stashed files; - answers that rely only on
git status --porcelain
will not tell you that you have commits that are not pushed yet to the remote.
Solution 10 - Git
Here's how I do it...
CHANGES=`git status | grep "working directory clean"`
if [ ! CHANGES -eq "" ] then
# do stuff here
else
echo "You have uncommitted changes..."
fi
Solution 11 - Git
This works nicely. It will also list the files affected:
if git diff-index --name-status --exit-code HEAD;
then
echo Git working copy is clean...;
else
echo ;
echo ERROR: Git working copy is dirty!;
echo Commit your changes and try again.;
fi;
Solution 12 - Git
Here is a nice set of Bash script functions that check if there is a diff, prints it to the user and prompts the user if they would like to commit changes before deployment. It is built for a Heroku and Python application, but it needs little change for any other application.
commit(){
echo "Please enter a commit message..."
read msg
git add . --all
git commit -am $msg
}
check_commit(){
echo ========== CHECKING FOR CHANGES ========
changes=$(git diff)
if [ -n "$changes" ]; then
echo ""
echo "*** CHANGES FOUND ***"
echo "$changes"
echo ""
echo "You have uncomitted changes."
echo "Would you like to commit them (y/n)?"
read n
case $n in
"y") commit;;
"n") echo "Changes will not be included...";;
*) echo "invalid option";;
esac
else
echo "... No changes found"
fi
}
deploy(){
check_commit
echo ========== DEPLOYING TO HEROKU ========
git push heroku master
heroku run python manage.py syncdb
}
You can copy from Gists on: https://gist.github.com/sshadmand/f33afe7c9071bb725105
Solution 13 - Git
nano checker_git.sh
paste this
#!/bin/bash
echo "First arg: $1"
cd $1
bob="Already up-to-date."
echo $bob
echo $(git pull) > s.txt
cat s.txt
if [ "$(cat s.txt)" == "$bob" ]
then
echo "up"
else
echo "not up"
fi
rm -rf st.txt
sh checker_git.sh gitpath
run Solution 14 - Git
Based on @storm_m2138 comment to @RyanMoon's answer (link) I am using the following in Powershell
.
function hasChanges($dir="."){ $null -ne (iex "git -C $dir status --porcelain --untracked-files=no") }
gci -Directory | ?{ hasChanges $_ } | %{ Write-Host "$_ has changes" }
gci -Directory | ?{ hasChanges $_ } | %{ iex "git -C $_ add -u"; iex "git -C $_ commit -m"Somemthing" }
Solution 15 - Git
The solution with Ansible:
- name: Bootstrap checks
block:
- shell: "git status --porcelain"
register: git_status
- fail:
msg: "Please clean git status: {{ git_status.stdout }}"
when: git_status.stdout != ""