How to check if there's nothing to be committed in the current branch?
Solution 1
An alternative to testing whether the output of git status --porcelain
is empty is to test each condition you care about separately. One might not always care, for example, if there are untracked files in the output of git status
.
For example, to see if there are any local unstaged changes, you can look at the return code of:
git diff --exit-code
To check if there are any changes that are staged but not committed, you can use the return code of:
git diff --cached --exit-code
Finally, if you want to know about whether there are any untracked files in your working tree that aren't ignored, you can test whether the output of the following command is empty:
git ls-files --other --exclude-standard --directory
Update: You ask below whether you can change that command to exclude the directories in the output. You can exclude empty directories by adding --no-empty-directory
, but to exclude all directories in that output I think you'll have to filter the output, such as with:
git ls-files --other --exclude-standard --directory | egrep -v '/$'
The -v
to egrep
means to only output lines that don't match the pattern, and the pattern matches any line that ends with a /
.
Solution 2
The return value of git status
just tells you the exit code of git status
, not if there are any modifications to be committed.
If you want a more computer-readable version of the git status
output, try
git status --porcelain
See the description of git status
for more information about that.
Sample use (script simply tests if git status --porcelain
gives any output, no parsing needed):
if [ -n "$(git status --porcelain)" ]; then
echo "there are changes";
else
echo "no changes";
fi
Please note that you have to quote the string to test, i.e. the output of git status --porcelain
. For more hints about test constructs, refer to the Advanced Bash Scripting Guide (Section string comparison).
Solution 3
If you are like me, you want to know if there are:
1) changes to existing files 2) newly added files 3) deleted files
and specifically do not want to know about 4) untracked files.
This should do it:
git status --untracked-files=no --porcelain
Here's my bash code to exit the script if the repo is clean. It uses the short version of the untracked files option:
[[ -z $(git status -uno --porcelain) ]] && echo "this branch is clean, no need to push..." && kill -SIGINT $$;
Solution 4
i'd do a test on this:
git diff --quiet --cached
or this to be explicit:
git diff --quiet --exit-code --cached
where:
--exit-code
Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and 0 means no differences.
--quiet
Disable all output of the program. Implies --exit-code
Solution 5
It's possible to combine git status --porcelain
with a simple grep
to perform the test.
if git status --porcelain | grep .; then
echo Repo is dirty
else
echo Repo is clean
fi
I use this as a simple one-liner sometimes:
# pull from origin if our repo is clean
git status --porcelain | grep . || git pull origin master
Add -qs
to your grep command to make it silent.
Related videos on Youtube
9nix00
Updated on May 11, 2022Comments
-
9nix00 about 2 years
The goal is to get an unambiguous status that can be evaluated in a shell command.
I tried
git status
but it always returns 0, even if there are items to commit.git status echo $? #this is always 0
I have an idea but I think it is rather a bad idea.
if [ git status | grep -i -c "[a-z]"> 2 ]; then code for change... else code for nothing change... fi
any other way?
update with following solve, see Mark Longair's post
I tried this but it causes a problem.
if [ -z $(git status --porcelain) ]; then echo "IT IS CLEAN" else echo "PLEASE COMMIT YOUR CHANGE FIRST!!!" echo git status fi
I get the following error
[: ??: binary operator expected
now, I am looking at the man and try the git diff.
===================code for my hope, and hope better answer======================
#if [ `git status | grep -i -c "$"` -lt 3 ]; # change to below code,although the above code is simple, but I think it is not strict logical if [ `git diff --cached --exit-code HEAD^ > /dev/null && (git ls-files --other --exclude-standard --directory | grep -c -v '/$')` ]; then echo "PLEASE COMMIT YOUR CHANGE FIRST!!!" exit 1 else exit 0 fi
-
Mark Longair over 13 yearsIn the updated section, it seems that you're not actually doing what eckes suggests in his answer - as he says, you need to put double-quotes around the
$(git status --porcelain)
. Also, if you want to put exclamation marks in your message, you'll need to use single quotes rather than double quotes - i.e. it should beecho 'PLEASE COMMIT YOUR CHANGE FIRST!!!'
instead -
eckes over 13 yearsas Mark says: you need to put double quotes around the
$(git status --porcelain)
, just as I told you! -
oberlies over 11 yearsThis questions would be a lot more useful, if it didn't include parts of answers.
-
MarcH over 10 years@9nix00 do what you have been told and edit and fix the bug in your shell script above: BUG: if [ -z $(some command) ] FIX: if [ -z "$(some command)" ]
-
jub0bs over 8 years
-
Chris B almost 8 yearsYour second attempt, using
git status --porcelain
was nearly correct. You were just missing some double quotes around the call togit status
. The first line should beif [ -z "$(git status --porcelain)" ]
. The double quotes ensure that the command's output (which may contain spaces) is treated as a single argument to the-z
test.
-
-
9nix00 over 13 yearshi,you give a good suggestion, I tried it, but in script ,it cause some problem , I improved it , if we use this if [ -z $(git status --porcelain) ]; it will get some error , [: ??: binary operator expected I find the manual and use this if [ -z $(git status --short) ]; this can work, thanks!
-
9nix00 over 13 yearssorry, there is still cause problem. when the commit is clean. use porcelain and short both ok. but when commit isn't clean. it will cause error. [: ??: binary operator expected. I think maybe we should try use base64 to encode it. let me try! downloading base64 command tools .... lol
-
9nix00 over 13 yearsit cause problem when the commit is not clean.
-
9nix00 over 13 yearsI leaned these tips and have an issue. that is , use git ls-files --other --exclude-standard --directory get the list include directories. is there someway exclude these directory?
-
9nix00 over 13 yearsyes , this is I want . and I update my post for new script-code. I thinks yours suggest is more strict logical although more code lol... and Hope better answers appear.
-
9nix00 over 13 yearsthis works fine except for new files . we can add this. if ! git ls-files --other --exclude-standard --directory | grep -c -v '/$' then exit 0 else echo "please commit your new file,if you don't want add it, please add it in git-ignore file." exit 1 fi
-
Arrowmaster over 13 yearsJust
if [ -n "$(git ls-files --others --exclude-standard)" ]
without any additional piping or greping should be enough to detect untracked files. -
mklement0 over 9 yearsGreat solution. For added robustness you could append
|| echo no
to the command substitution so that the workspace isn't mistakenly reported clean ifgit status
fails fundamentally. Also, your code is (commendably) POSIX-compliant, but since you link to a bash guide, let me add that if you use bash's[[ ... ]]
instead of the POSIX-compatible[ ... ]
, you do not need to double-quote the command substitution (though it does no harm):[[ -z $(git status --porcelain) ]]
. -
mklement0 over 9 years+1 for
—untracked-files=no
; I think your test can be simplified to[[ -z $(git status --untracked-files=no --porcelain) ]]
.git status
shouldn't write to stderr, unless something fundamental goes wrong - and then you do want to see that output. (If you wanted more robust behavior in that event, append|| echo no
to the command substitution so that the test for cleanness still fails). String comparisons / the-z
operator can deal with multi-line strings - no need fortail
. -
mklement0 over 9 years+1 for elegance; slight caveat: should
git status
fail fatally (e.g., a corrupted repo), your test will mistakenly report a clean workspace. One option is to usegit status --porcelain 2>&1
, but that would 'eat' the error message if you used grep with-q
. (Dealing with that would lose the elegance:(git status --porcelain || echo err) | grep -q .
) -
moodboom over 9 yearsthanks @mklement0, even a bit shorter:
[[ -z $(git status -u no --porcelain) ]]
-
Mark Longair over 9 years@albfan: it's in the git-diff man page: "Make the program exit with codes similar to diff(1). That is, it exits with 1 if there were differences and 0 means no differences."
-
albfan over 9 yearsJust to point out, It's been there at least since 2007 13da0fc0, really handy for shell scripts, and fully compatible with older versions of git
-
moodboom about 9 yearscorrection: my shorter version actually just checks the status on the file named "no"! Bzzt. Should be:
[[ -z $(git status -uno --porcelain) ]]
-
mklement0 about 9 yearsI appreciate the follow-up; that's a subtle bug - the lesson is that short options with optional arguments must have the argument appended directly, with no whitespace in between. How about incorporating the corrected short version directly into your answer?
-
Charles Merriam about 9 yearsOddly failing. My test is
git diff --exit-code || echo OK
or more complex variants. I should get different answered. -
phs about 8 years
--quiet
(which implies--exit-code
) also silences output, for those who only want the exit code. -
VasiliNovikov over 6 yearsalternatively, one can write:
test -z "$(git status --porcelain)" || git pull origin master
-
alinsoar about 6 years@eckes I started a new repo a few days ago, I added a commit, I tried to write some pre-commit-hook and check what is to be committed and what you say did not work on my case.
-
Alexander Mills almost 6 yearsusing
if ! git diff --quiet; then
is cleaner and more performant (I think). In other words, use the exit code, not the stdout. -
Martin von Wittich over 4 years@AlexanderMills
git diff --quiet
behaves differently fromgit status --porcelain
for cached changes. -
tripleee about 4 yearsThis will basically be undefined if there are more than 255 lines of output.
-
Skeeve about 4 yearsWell spotted, Thanks!
-
cigien over 2 yearsWhen linking to your own site or content (or content that you are affiliated with), you must disclose your affiliation in the answer in order for it not to be considered spam. Having the same text in your username as the URL or mentioning it in your profile is not considered sufficient disclosure under Stack Exchange policy.
-
VonC about 2 yearsExcellent: I used
git commit -a --dry-run --short|wc -l
. If not 0, I know some work is still in progress.