📖 Scenario: You are working on a team project where every commit message must start with a ticket number like PROJ-123 followed by a short description. This helps everyone understand what the commit is about and links it to the right task.
🎯 Goal: Create a commit-msg Git hook script that checks if the commit message starts with a ticket number pattern PROJ- followed by digits. If the message does not match, the commit should be rejected with a clear error message.
📋 What You'll Learn
Create a commit-msg hook script file
Check the commit message file content for the pattern PROJ- followed by digits at the start
Reject the commit if the pattern is missing with a message
Allow the commit if the pattern is present
💡 Why This Matters
🌍 Real World
Teams use commit-msg hooks to enforce commit message standards, improving project tracking and collaboration.
💼 Career
Knowing how to write Git hooks is useful for DevOps roles and software developers to maintain code quality and workflow consistency.
Progress0 / 4 steps
1
Create the commit-msg hook script file
Create a file named commit-msg inside the .git/hooks/ directory with the first line #!/bin/sh to specify the shell interpreter.
Git
Hint
The first line of a shell script should be #!/bin/sh to tell Git which shell to use.
2
Read the commit message from the file
Add a line to read the commit message from the file path passed as the first argument $1 into a variable named commit_msg.
Git
Hint
Use commit_msg=$(cat "$1") to read the commit message content.
3
Check if commit message starts with 'PROJ-' followed by digits
Add an if statement that uses grep -qE to check if commit_msg starts with the pattern PROJ- followed by one or more digits. If it does not match, print Error: Commit message must start with PROJ- followed by ticket number. and exit with status 1.
Git
Hint
Use grep -qE '^PROJ-[0-9]+' to check the pattern at the start of the message.
4
Allow commit if message is valid
Add a line to exit with status 0 at the end of the script to allow the commit if the message passed the check.
Git
Hint
Use exit 0 to indicate success and allow the commit.
Practice
(1/5)
1. What is the main purpose of a commit-msg hook in Git?
easy
A. To check and validate the commit message before the commit is saved
B. To automatically push commits to the remote repository
C. To merge branches after a commit
D. To delete old commits from history
Solution
Step 1: Understand the role of Git hooks
Git hooks are scripts that run at specific points in Git's workflow to automate tasks or checks.
Step 2: Identify the function of the commit-msg hook
The commit-msg hook runs after a commit message is entered but before the commit is finalized, allowing validation of the message.
Final Answer:
To check and validate the commit message before the commit is saved -> Option A
Quick Check:
commit-msg hook = validate commit message [OK]
Hint: commit-msg hook validates commit messages before saving [OK]
Common Mistakes:
Confusing commit-msg hook with pre-push or post-commit hooks
Thinking it automatically pushes commits
Assuming it modifies commit history
2. Which of the following is the correct way to make a commit-msg hook executable on a Unix-like system?
easy
A. chmod 000 .git/hooks/commit-msg
B. chmod +x .git/hooks/commit-msg
C. git commit --hook commit-msg
D. git hook enable commit-msg
Solution
Step 1: Understand file permissions for hooks
Git hooks are scripts stored in the .git/hooks directory and must be executable to run.
Step 2: Identify the correct command to make a file executable
The command chmod +x adds execute permission to the file, enabling the hook to run.
Final Answer:
chmod +x .git/hooks/commit-msg -> Option B
Quick Check:
chmod +x = make executable [OK]
Hint: Use chmod +x to enable hook execution [OK]
Common Mistakes:
Using chmod 000 which removes all permissions
Trying git commands that don't exist for hooks
Not setting executable permission, so hook won't run
3. Given this commit-msg hook script snippet, what will happen if the commit message is empty?
#!/bin/sh
if [ -z "$1" ]; then
echo "Error: Commit message is empty." >&2
exit 1
fi
medium
A. The commit will be blocked with an error message
B. The commit message will be replaced with a default message
C. The commit will proceed without any message
D. The script will ignore the message and exit with 0
Solution
Step 1: Analyze the script condition
The script checks if the first argument $1 is empty using [ -z "$1" ]. In the commit-msg hook, $1 is the path to a temporary file containing the proposed commit message.
Step 2: Understand Git's behavior
Since $1 is the file path, it is never empty, so the condition [ -z "$1" ] is false. However, the script as written incorrectly checks the variable $1 instead of the content of the file. To check if the commit message is empty, the script should check the content of the file, e.g., if [ ! -s "$1" ] or if [ -z "$(cat $1)" ]. As is, the script will not block empty commit messages.
Final Answer:
The commit will proceed without any message -> Option C
Quick Check:
$1 is file path, not content; checking it for emptiness is incorrect [OK]
Hint: $1 is file path, not content; check with `cat $1` [OK]
Common Mistakes:
Thinking exit 1 allows commit to proceed
Assuming the message is replaced automatically
Confusing $1 with the commit message text instead of file path
4. You wrote this commit-msg hook to reject messages shorter than 10 characters:
#!/bin/sh
MSG=$(cat "$1")
if [ ${#MSG} -lt 10 ]; then
echo "Commit message too short." >&2
exit 1
fi
But it does not block short messages. What is the likely problem?
medium
A. The hook script is not executable
B. The commit message file path is incorrect
C. The shell does not support ${#MSG} syntax for string length
D. The script should use -z instead of -lt
Solution
Step 1: Check the script logic
The script correctly reads the commit message and checks its length using ${#MSG} which is valid in sh/bash.
Step 2: Consider common hook issues
If the hook is not executable, Git will ignore it and allow commits without running the script.
Final Answer:
The hook script is not executable -> Option A
Quick Check:
Non-executable hook = script not run [OK]
Hint: Make sure hook script is executable with chmod +x [OK]
Common Mistakes:
Assuming ${#MSG} is invalid in shell
Confusing file path with message content
Using wrong test operators like -z for length comparison
5. You want to enforce commit messages to start with a ticket number like PROJ-123: followed by a space and description. Which commit-msg hook snippet correctly validates this pattern?
hard
A. #!/bin/sh
MSG=$(cat "$1")
if echo "$MSG" | grep -qE '^PROJ-[a-z]+: .+'; then
echo "Invalid ticket format" >&2
exit 1
fi
B. #!/bin/sh
MSG=$(cat "$1")
if echo "$MSG" | grep -q '^PROJ-[0-9]+: .+'; then
exit 1
fi
C. #!/bin/sh
if [ -z "$1" ]; then
echo "Empty message" >&2
exit 0
fi
D. #!/bin/sh
MSG=$(cat "$1")
if ! echo "$MSG" | grep -qE '^PROJ-[0-9]+: .+'; then
echo "Commit message must start with 'PROJ-<number>: '" >&2
exit 1
fi
Solution
Step 1: Understand the required pattern
The commit message must start with 'PROJ-' followed by digits, a colon, a space, then some text.
Step 2: Analyze each option's regex and logic
#!/bin/sh
MSG=$(cat "$1")
if ! echo "$MSG" | grep -qE '^PROJ-[0-9]+: .+'; then
echo "Commit message must start with 'PROJ-<number>: '" >&2
exit 1
fi uses grep -qE with '^PROJ-[0-9]+: .+' and exits with error if pattern not matched, which is correct. #!/bin/sh
MSG=$(cat "$1")
if echo "$MSG" | grep -q '^PROJ-[0-9]+: .+'; then
exit 1
fi exits on match (wrong logic). #!/bin/sh
if [ -z "$1" ]; then
echo "Empty message" >&2
exit 0
fi exits 0 on empty message (wrong). #!/bin/sh
MSG=$(cat "$1")
if echo "$MSG" | grep -qE '^PROJ-[a-z]+: .+'; then
echo "Invalid ticket format" >&2
exit 1
fi checks for lowercase letters after PROJ- which is incorrect.
Final Answer:
#!/bin/sh
MSG=$(cat "$1")
if ! echo "$MSG" | grep -qE '^PROJ-[0-9]+: .+'; then
echo "Commit message must start with 'PROJ-<number>: '" >&2
exit 1
fi -> Option D