How can I check in a Bash script if my local Git repository has changes?

Git

Git 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:
  1. 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 to origin/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 from master branch.
  • git diff --exit-code origin/master..master > /dev/null will error if your current master branch differs from origin/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

run sh checker_git.sh gitpath

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 != ""

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionkmindiView Question on Stackoverflow
Solution 1 - GitryanmoonView Answer on Stackoverflow
Solution 2 - GitCascabelView Answer on Stackoverflow
Solution 3 - GitArrowmasterView Answer on Stackoverflow
Solution 4 - GitblackjacxView Answer on Stackoverflow
Solution 5 - GitLeon WaldmanView Answer on Stackoverflow
Solution 6 - GitAndrewDView Answer on Stackoverflow
Solution 7 - GitolegtaranenkoView Answer on Stackoverflow
Solution 8 - GitSeamusView Answer on Stackoverflow
Solution 9 - GitjtomplView Answer on Stackoverflow
Solution 10 - GitJader FeijoView Answer on Stackoverflow
Solution 11 - GitrobertberringtonView Answer on Stackoverflow
Solution 12 - GitSeanView Answer on Stackoverflow
Solution 13 - GitRobert AView Answer on Stackoverflow
Solution 14 - GitDennisView Answer on Stackoverflow
Solution 15 - GitThomas DecauxView Answer on Stackoverflow