commit-msg hook for message validation in Git - Time & Space Complexity
We want to understand how the time to check a commit message grows as the message gets longer.
Specifically, how does the validation process scale with message size?
Analyze the time complexity of the following commit-msg hook script.
#!/bin/sh
commit_msg_file=$1
# Read the commit message
commit_msg=$(cat "$commit_msg_file")
# Check if message matches pattern
if ! echo "$commit_msg" | grep -qE '^[a-z]+: .{10,}'; then
echo "Error: Commit message must start with type and have at least 10 characters."
exit 1
fi
This script reads the commit message and checks if it starts with a word and a colon, followed by at least 10 characters.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The grep command scans the entire commit message text.
- How many times: It processes each character once to match the pattern.
The time to check grows roughly in direct proportion to the length of the commit message.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 characters | About 10 checks |
| 100 characters | About 100 checks |
| 1000 characters | About 1000 checks |
Pattern observation: The work grows linearly as the message gets longer.
Time Complexity: O(n)
This means the time to validate grows directly with the length of the commit message.
[X] Wrong: "The validation time is always constant because the pattern is simple."
[OK] Correct: Even a simple pattern check must look at each character, so longer messages take more time.
Understanding how simple checks scale helps you write efficient hooks and scripts that keep your workflow smooth.
"What if the commit-msg hook also checked multiple patterns one after another? How would the time complexity change?"
Practice
commit-msg hook in Git?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 AQuick Check:
commit-msg hook = validate commit message [OK]
- Confusing commit-msg hook with pre-push or post-commit hooks
- Thinking it automatically pushes commits
- Assuming it modifies commit history
commit-msg hook executable on a Unix-like system?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 commandchmod +xadds execute permission to the file, enabling the hook to run.Final Answer:
chmod +x .git/hooks/commit-msg -> Option BQuick Check:
chmod +x = make executable [OK]
- 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
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
Solution
Step 1: Analyze the script condition
The script checks if the first argument$1is empty using[ -z "$1" ]. In the commit-msg hook,$1is the path to a temporary file containing the proposed commit message.Step 2: Understand Git's behavior
Since$1is the file path, it is never empty, so the condition[ -z "$1" ]is false. However, the script as written incorrectly checks the variable$1instead 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" ]orif [ -z "$(cat $1)" ]. As is, the script will not block empty commit messages.Final Answer:
The commit will proceed without any message -> Option CQuick Check:
$1is file path, not content; checking it for emptiness is incorrect [OK]
- Thinking exit 1 allows commit to proceed
- Assuming the message is replaced automatically
- Confusing $1 with the commit message text instead of file path
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?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 AQuick Check:
Non-executable hook = script not run [OK]
- Assuming ${#MSG} is invalid in shell
- Confusing file path with message content
- Using wrong test operators like -z for length comparison
PROJ-123: followed by a space and description. Which commit-msg hook snippet correctly validates this pattern?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 DQuick Check:
grep -qE '^PROJ-[0-9]+: .+' = valid pattern check [OK]
- Reversing logic: exiting on match instead of no match
- Using wrong regex for digits or colon
- Exiting with 0 on error conditions
Ready to prove it?
Pass the test and this pattern moves unknown
