Get commit message in Git hooks - bash

I would like to create commit-msg hook to check the commit message with some regex. So I wrote this bash script in the hooks folder:
#!/bin/sh
valid_commit_msg_regex="^[0-9]+:[a-zA-Z0-9-_ ]{20,150}$"
message="This commit violates the commit message rules. Please rename your commit."
if [[ ! $(cat $1) =~ $valid_commit_msg_regex ]]
then
echo "$message"
exit 1
fi
exit 0
But when I try to write git commit -m "9: texttexttexttexttexttexttexttext" it doesn't work properly and I get the error message from script.
Does anyone has an idea what's gone wrong?

I see two issues:
Double brackets [[ comes from bash. Change shebang to #!/bin/bash
in [a-zA-Z0-9-_ ] you have to escape - with \-

Related

Bash Script - Determine whether to add file to Git commit [duplicate]

This question already has answers here:
How do I set a variable to the output of a command in Bash?
(15 answers)
Closed 5 months ago.
This is probably a simple one for a bash scripter, which I am not.
I'm running a cron job that downloads some data, and then depending on that data, may or may not modify a second file. After the job, I want to git commit one or both files. For the conditional commit, I tried this in a .sh script:
# attempt to capture whether MyNotes.txt was changed
# by counting lines in git status output
mywc=(git status -s MyNotes.txt | wc -l)
echo $mywc found!
if [ $mywc = 1 ]; then
echo Add file for commit
else
echo Nothing to add
fi
I'm pretty much getting nowhere; this thing seems to fail on the first line with syntax error near unexpected token '|'. If I run git status -s MyNotes.txt | wc -l on the command line, I get the numeric output I expect.
What am I doing wrong and how can I make this work?
If there's a more elegant way to determine whether a file changed, feel free to share.
Also, for my edification, how could I get this to work without the interim mywc variable? I.e., if I wanted to just do the command within the if, something like this:
if [[ $(git status -s MyNotes.txt | wc -l) = 1 ]]; then
...
Thanks!
What am I doing wrong and how can I make this work?
put a dollar before parenthesis.
foo=$(command)
The thing you are using looks like a bash array
declare -a letters=(a b c d)
If there's a more elegant way to determine whether a file changed, feel free to share.
Consider this:
$ git diff -s --exit-code README.md || echo has changed
has changed
$ git checkout README.md
Updated 1 path from the index
$ git diff -s --exit-code README.md || echo has changed
The OR (||) runs if the first command exits with a non-zero code.
Same thing essentially:
$ false || echo false exits with 1
false exits with 1
$ true || echo will not trigger
An aspect of bash that people overlook is that [[, ]], [ and ] are separate commands. They have return codes too. With this knowledge, you can leverage the return codes with if and any other command.
$ if true; then echo yes; else echo no; fi
yes
$ if false; then echo yes; else echo no; fi
no
So for detecting changes in a tracked file:
$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi
same as in index
$ echo 123 >> README.md
$ if git diff -s --exit-code README.md; then echo same as in index; else echo changed; fi
changed
With all of that said...
Just add the file. You don't need to check anything. If it hasn't changed, nothing will happen.
$ echo foo >> myfile
$ git add myfile
$ git commit -m 'maybe changed' myfile
[master b561cc1] maybe changed
1 file changed, 1 insertion(+), 1 deletion(-)
$ git add myfile
$ git commit -m 'maybe changed' myfile
no changes added to commit (use "git add" and/or "git commit -a")
if you need to avoid a non-zero exit code (such as with set -e), just put a || true after the command that you want to ignore the exit status of:
$ cat foo.sh
#!/bin/basho
set -e
echo foo >> myfile
git add myfile
git commit -m 'maybe changed' myfile
git add myfile
git commit -m 'maybe changed' myfile > /dev/null || true
echo no error here. it\'s fine..
false
echo fill never reach this.
Try running that script and see what happens
I search for a way for checking if file changed.
git diff --exit-code -s <path>
Now the bash scripter knows that every command returns a status code which can be checked with $?. In case everything went smoothly, 0 is returned. In that case we get 0 if file is not changed.
Every bash scripter knows too that you can use that with && and || operators (because of lazy evaluation) to write such construct:
git diff --exit-code -s <path> && echo "should add file"
About your edification, what you wrote is perfectly fine!
As CryptoFool pointed out in a comment, I failed to include a $ in my variable assignment. Simple fix in the first line of my script:
mywc=$(git status -s MyNotes.txt | wc -l)
As matt pointed out in a subsequent comment, doing a git add on a file that hasn't changed has no effect. It won't stage the file for commit. So instead of doing conditional logic to determine whether to git add myfile.txt, I'll just blindly execute git add myfile.txt, which will either stage the file if there are changes, or do nothing if there are no changes. Therefore, my entire script can be replaced with one line:
git add MyNotes.txt

git run command if nothing to commit

This question is probably about bash as much as it is about git.
How do I run a command if there is nothing to commit? I did the following, but it runs the echo hello command even if there is nothing to commit:
if git diff --exit-code; then
echo hello
fi
Coming from a python background, what I assumed would happen is if git ... command is empty then the stuff inside the if statement would not execute. I have confirmed that git diff --exit-code returns nothing.
TLDR: Your current code echoes hello in the success case, not the failure case.
We can fix this by inverting the condition, so it becomes:
if ! git diff --exit-code; then
echo hello
fi
I'd recommend having a look at How to check the exit status using an 'if' statement which provides a few options for how to do this.
Try something like this:
git diff --exit-code # exit code will be "0" if no changes
if [ $? -gt 0 ]; then
echo hello
fi

While loop with if statement plus result of variable

I was doing a script for myself to summarize commands I use daily in one handy script. So basically I ended doing it with a conditional checking if the .git folder exists first but I'd like to make it more interesting and like so understand better the loop. My desire is to have a variable like:
"output=$(git status)" and if the result is 0, continue depending on the statement. If the result is other than 0, break the loop and end the script with a message like "the actual directory hasn't a .git repo".
I let you my first idea of it but without the git status as I don't know how to add it neither where to. Thank you guys!
set -e
gitrepo=true
while [ $gitrepo == true ]; do
if [[ $? -ne 0 ]]; then
echo "not a git directory"
$gitrepo=false
else
read -p "Commit message: " commit
git commit -am "$commit"
fi
done
Try this: I did as Cyrus suggested:
#!/usr/bin/env bash
set -e
gitrepo=True
while [[ $gitrepo ]]; do
if [[ ! $? ]]; then
echo "not a git directory"
gitrepo=False
else
read -p "Commit message: " -r commit
git commit -am "$commit"
exit 0
fi
done

How to make a git pre-commit hook that checks the commit message?

I have a git commit hook script, that checks the commit message, and if the message does not contain the word "updated", the script should reject the commit.
#!/bin/bash
read -p "Enter a commit message: " message
if [[ ${message} != *"updated"* ]];then
echo "Your commit message must contain the word 'updated'"
else
git commit -m "$message"
fi
How to make this hook automatically execute if I try to push some files in my local repo using the command
git commit -m "updated:something"
My idea is to make it not like "run this script to do commit", but rather when you open the console and try to make a commit and entering the commit message, the script will check your commit message automatically and pass it or reject it.
Taking commit-msg for example.
#!/bin/bash
MSG="$1"
if ! grep -qE "updated" "$MSG";then
cat "$MSG"
echo "Your commit message must contain the word 'updated'"
exit 1
fi
chmod 755 commit-msg and copy it as .git/hooks/commit-msg.

unexpected operator [: git: in bourne shell script 'if' conditional statement

I've watched an excellent shell scripting course through a multitude of videos. Now that I think I am fairly familiar with the Bourne shell, I decided to write my first shell script.
Script goal: check if git working directory is clean. If so, overwrite working directory to a branch named deployment. Finally, push the deployment branch to origin.
I ended up with this code:
#!/bin/sh
######################################################
# Deploys working directory to git deployment branch.
# Requires that the working directory is clean.
######################################################
#check if the working directory is clean
if [ git diff-index --quiet HEAD ]
then
if [ git branch -f deployment ]
then
if [ git push origin deployment ]
then
echo
echo "OK. Successfully deployed to git deployment branch."
echo
exit 0 #success
else
echo
echo "Error: failed to push deployment branch to origin."
echo
exit 1 #failure
fi
else
echo
echo "Error: failed to create or overwrite deployment branch."
echo
exit 1 #failure
fi
else
echo
git status #show the status of the working directory
echo
echo "Error: working directory is not clean. Commit your changes first..."
echo
exit 1 #failure
fi
Unfortunately, this seems to give me an error: ./tools/deploygit: 9: [: git: unexpected operator
Why is this so? What operator am I using in if [ git diff-index --quiet HEAD ] that is unexpected?
As a bonus, do you have any suggestions or tips on how to improve the efficiency, logic or readability of this script?
In this statement:
if [ git diff-index --quiet HEAD ]
The [ is an alias for the test command, so what you're actually running is...
if test git diff-index --quiet HEAD ]
...which isn't what you mean. You don't need to use the test command in order to evaluate the result of a command; you should just do this:
if git diff-index --quiet HEAD
Take a look at the documentation for the if command:
$ help if
if: if COMMANDS; then COMMANDS; [ elif COMMANDS; then COMMANDS; ]... [ else COMMANDS; ] fi
The conditional argument to the if statement is command. Normally, the test command is used to make it look like other languages, but you can put any command there. Things that exit with a return code of 0 evaluate to true and anything else evaluates to false.

Resources