[Article by Paolo Tagliani]
Git is one of the fundamental tools we (developers) use every day and with which (at least for me), have a love-hate relationship.
Have you ever deleted all the changes you just made on a project due to a wrong git command? π I did it.
This article is a collection of useful git commands you should know that will improve your day-to-day life.
In this article we will explore three useful git features:
Let's start.
Git Alias: create your own git commands
Git aliases allow you to create custom shortcuts for frequently used or complex Git commands. They can significantly improve your workflow efficiency.
How to add
Add aliases to your Git config either through commands or by editing ~/.gitconfig
:
# Command line setup
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
Or in ~/.gitconfig
:
[alias]
co = checkout
br = branch
ci = commit
st = status
Useful Aliases
1. Pretty Log Output
[alias]
lg = log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr) %C(bold blue)<%an>%Creset' --abbrev-commit --date=relative
hist = log --pretty=format:'%h %ad | %s%d [%an]' --graph --date=short
Below is an example of the lg output using the lazygit repo:
Work in Progress
[alias]
# Quickly save work in progress
wip = "!git add -A && git commit -m 'WIP: Work in progress'"
# Undo last WIP commit
unwip = "!git log -n 1 | grep -q -c 'WIP' && git reset HEAD~1"
When you commit WIP, don't forget to edit your commits with git amend
or include multiple commits into one using git rebase
(maybe that's for the next article).
Git Bisect: blaming the commit that breaks everything
You pulled the latest version of develop into your branch, and things arenβt working as expected. How to identify which commit broke your functionality? Git Bisect to the rescue.
The concept is simple. You provide 2 commits: the initial bad
and initial good
.
- Git divides the space between those commits in 2.
- Takes the commit that divides the two halves and places your
HEAD
there. You test it and inform git if the bug is still there withgit bisect good
orgit bisect bad
. - Depending on the result, one of the two halves is marked as good, and the bad one is divided again in two.
- The process repeats until there is only one commit left, which is the source of your error.
A practical example
Here I created a simple calculator library, where I added an error hidden in one of the commits.
I need to identify what was the commit breaking the current implementation. Git bisect is the right tool for this job.
Watch the following screencast to see git bisect in action. The bottom terminal shows tests running in watch mode, while the top shows the current list of commits:
Here what I did to bisect it:
- I start bisecting with
git bisect start
- It needs the first good and bad commits: I indicated the first one
911bbde
as good withgit bisect good 911bbde
. The bad one is the currentHEAD
,git bisect bad head
. - The bisecting process starts and I just need to watch the tests and provide
git bisect good/bad
depending if the tests pass or not. Note that git will tell you how many steps in the process are remaining withroughly X steps
. - After some passes, it correctly identifies that
64f1d0beb41fc03ae38c42b01fa2a0907cd236b7
is the first bad commit.
A deeper look
To help you visualize, here is how I mentally map the bisect process.
1. Identify good and bad (the boundaries)
2. Start bisecting in the middle of the interval
If the current one is good, it means that all the commits before are good.
3. Divide the bad interval and re-evaluate
4. Detect the bad commit
Git Hooks: customize git with scripting
Hooks are scripts that are executed on some events. Here are some common types of hooks:
- Pre-commit Hook: Runs before a commit is created
- Prepare-commit-msg Hook: Runs before the commit message editor is launched
- Commit-msg Hook: Runs after the commit message is created
- Pre-push Hook: Runs before a push is executed
- Post-commit Hook: Runs after a commit is created
Why are those useful? They allow you to run ANY script before operating git commands, and can help streamline your workflow.
** Important Note: Remember that hooks are local by default - they need to be explicitly shared and set up on each developer's machine.
Useful examples
TODO checks
I often leave // TODO:
comments in my code. I use them for everything: remembering to delete code, planning refactors, or noting implementation details that weren't core to the feature I was working on.
I don't want these TODOs to be committed to the main repository, so I created a git hook that will warn me.
Here is the hook:
#!/bin/sh
# Path: .git/hooks/pre-commit-todo
# Get all staged files
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACM | grep -E '\.(js|jsx|ts|tsx|py|rb|php|go|java|cpp|h)$')
# Skip if no relevant files are staged
if [ -z "$STAGED_FILES" ]; then
exit 0
fi
# Function to check for TODOs
check_todos() {
local file="$1"
local todos=$(git show ":$file" | grep -n "TODO\|FIXME" || true)
if [ ! -z "$todos" ]; then
echo "β οΈ Found TODOs in $file:"
echo "$todos" | while IFS= read -r line; do
echo " Line $line"
done
return 1
fi
return 0
}
# Track if we found any TODOs
FOUND_TODOS=0
# Check each staged file
for file in $STAGED_FILES; do
if check_todos "$file"; then
continue
else
FOUND_TODOS=1
fi
done
if [ $FOUND_TODOS -eq 1 ]; then
echo "----------------------------------------"
echo "β Error: Found TODO comments in staged files"
echo "Options:"
echo "1. Remove or resolve the TODOs"
echo "2. Create an issue for each TODO"
echo "3. Use git commit --no-verify to bypass this check"
echo "----------------------------------------"
exit 1
fi
# Silent success - no output if no TODOs found
exit 0
And here is what git will tell me in case I have TODOs in my staged files:
β οΈ Found TODOs in src/components/Payment.js:
Line 45: // TODO: Implement payment validation
Line 67: // TODO: Add error handling for failed payments
β οΈ Found TODOs in src/utils/api.js:
Line 23: // FIXME: Need to handle token expiration
----------------------------------------
β Error: Found TODO comments in staged files
Options:
1. Remove or resolve the TODOs
2. Create an issue for each TODO
3. Use git commit --no-verify to bypass this check
----------------------------------------
[Error: Git commit aborted due to TODO comments]
Force conventional commits
I heavily use conventional commits, here's a git hook that forces me to have my commit messages formatted in that way:
#!/bin/sh
# .git/hooks/commit-msg
commit_msg_file=$1
commit_msg=$(cat $commit_msg_file)
# Enforce conventional commit format
if ! echo "$commit_msg" | grep -qE "^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .+"; then
echo "β Error: Invalid commit message format"
echo "----------------------------------------"
echo "Your message: '$commit_msg'"
echo "Required format: <type>(<scope>): <description>"
echo "Valid types: feat|fix|docs|style|refactor|test|chore"
echo "Example: feat(user-auth): add password reset functionality"
echo "----------------------------------------"
exit 1
fi
Here's what I see when the commit message is not right:
β Error: Invalid commit message format
----------------------------------------
Your message: 'Added new login feature'
Required format: <type>(<scope>): <description>
Valid types: feat|fix|docs|style|refactor|test|chore
Example: feat(user-auth): add password reset functionality
----------------------------------------
[Error: Git commit aborted due to invalid commit message format]
Key Takeaways
- Git aliases can significantly speed up your daily Git commands
- Git bisect is a powerful tool for finding problematic commits
- Git hooks help automate checks and maintain code quality
- All these features can be customized to match your team's workflow
Top comments (0)