Tech Tips Monday: 7 Advanced Git Techniques That Will Transform Your Workflow
Starting a new week with Tech Tips Monday. Today’s focus is on advanced Git workflows: techniques that go beyond commit and push, optimizing collaboration, debugging, and maintaining a clean history.
1. Interactive Rebase for a Clean History
Interactive rebase allows you to rewrite history before pushing, creating organized and meaningful commits.
Reorganizing Commits:
# Interactive rebase of the last 5 commits
git rebase -i HEAD~5
# In the editor you'll see:
# pick a1b2c3d feat: add user authentication
# pick d4e5f6g fix: typo in validation
# pick h7i8j9k feat: add password reset
# pick k0l1m2n wip: debugging session
# pick n3o4p5q fix: authentication edge case
Available Commands:
# pick (p) - use commit as is
# reword (r) - change commit message
# edit (e) - pause to modify commit
# squash (s) - combine with previous commit
# fixup (f) - combine without message
# drop (d) - delete commit
# exec (x) - execute shell command
# Example of reorganization:
pick a1b2c3d feat: add user authentication
fixup n3o4p5q fix: authentication edge case
reword d4e5f6g fix: typo in validation
squash h7i8j9k feat: add password reset
drop k0l1m2n wip: debugging session
Autosquash Workflow:
# During development, mark commits for automatic squash
git commit -m "feat: add feature X"
git commit -m "fixup! feat: add feature X" # Will auto-squash
git commit -m "squash! feat: add feature X" # Will auto-squash with message
# When rebasing, they will be automatically organized
git rebase -i --autosquash main
Recommended Configuration:
# Enable autosquash by default
git config --global rebase.autosquash true
# Enable auto-stash before rebase
git config --global rebase.autoStash true
2. Git Worktrees for Multiple Branches Simultaneously
Work on multiple branches without changing context or constantly stashing.
Basic Setup:
# Create additional worktree
git worktree add ../mi-proyecto-feature-x feature-x
git worktree add ../mi-proyecto-hotfix hotfix/critical-bug
# Now you have separate directories:
# ~/Projects/mi-proyecto/ (main branch)
# ~/Projects/mi-proyecto-feature-x/ (feature-x branch)
# ~/Projects/mi-proyecto-hotfix/ (hotfix branch)
# List active worktrees
git worktree list
Practical Workflow:
# Terminal 1: Development on feature
cd ~/Projects/mi-proyecto-feature-x
npm run dev
# Terminal 2: Urgent hotfix
cd ~/Projects/mi-proyecto-hotfix
npm run test
git commit -m "fix: critical security issue"
git push
# Terminal 3: Code review
cd ~/Projects/mi-proyecto
git worktree add ../review-pr-123 pr-123
cd ../review-pr-123
npm install
npm test
Cleanup:
# Remove worktree when done
git worktree remove ../mi-proyecto-feature-x
# Force removal if there are uncommitted changes
git worktree remove --force ../mi-proyecto-hotfix
# Clean up orphaned references
git worktree prune
Advantages:
- Multiple branches running simultaneously
- No loss of development context
- Ideal for code reviews without interrupting current work
- Parallel testing on different branches
3. Git Bisect for Regression Debugging
Automatically find the commit that introduced a bug using binary search.
Basic Usage:
# Start bisect
git bisect start
# Mark current commit as bad
git bisect bad
# Mark last known good commit (e.g., previous tag)
git bisect good v1.2.0
# Git automatically checks out the midpoint
# Test if the bug exists
npm test
# If tests pass:
git bisect good
# If tests fail:
git bisect bad
# Repeat until the guilty commit is found
# Git will show: "X is the first bad commit"
Automated Bisect:
# Automate with test script
git bisect start HEAD v1.2.0
git bisect run npm test
# Or with custom command
git bisect run ./scripts/check-regression.sh
# Script check-regression.sh:
#!/bin/bash
npm test
exit $? # 0 = good, 1 = bad
Bisect with Custom Terms:
# Use more descriptive terms than good/bad
git bisect start --term-new=broken --term-old=working
git bisect broken # instead of 'bad'
git bisect working # instead of 'good'
Skip Commits:
# If a commit doesn't compile or can't be tested
git bisect skip
# Skip a range of commits
git bisect skip v1.0..v1.1
Finish Bisect:
# Once the problem is identified
git bisect reset # Returns to original branch
# View bisect log
git bisect log
# Replay previous session
git bisect replay bisect.log
4. Git Reflog: The Lifesaver for Mistakes
Reflog records all changes to HEAD, allowing you to recover “lost” commits.
Common Use Cases:
# View complete HEAD history
git reflog
# Output:
# a1b2c3d HEAD@{0}: commit: feat: add feature
# d4e5f6g HEAD@{1}: rebase -i: squash commits
# h7i8j9k HEAD@{2}: reset: moving to HEAD~1
# k0l1m2n HEAD@{3}: commit: wip: debugging
# Recover commit after accidental reset
git reset --hard HEAD@{3}
# Recover accidentally deleted branch
git checkout -b recovered-branch HEAD@{5}
Searching in Reflog:
# Search commits by message
git reflog | grep "feat: important feature"
# View reflog of specific branch
git reflog show feature-branch
# Reflog with readable dates
git reflog --date=relative
# Reflog with more details
git reflog show --all
Recovery of Common Scenarios:
# Scenario 1: Rebase went wrong
git reflog
git reset --hard HEAD@{before-rebase}
# Scenario 2: Accidentally deleted branch
git reflog | grep "branch-name"
git checkout -b branch-name
```<commit-hash>
# Scenario 3: Lost Commit After Reset
git reflog
git cherry-pick <commit-hash>
# Scenario 4: Bad Merge
git reflog
git reset --hard HEAD@{before-merge}
Retention Configuration:
# Change reflog retention time (default 90 days)
git config --global gc.reflogExpire 180.days
git config --global gc.reflogExpireUnreachable 90.days
5. Advanced Git Stash for Context Switching
Manage work-in-progress changes in an organized way.
Stash with Descriptive Names:
# Basic stash with message
git stash push -m "WIP: user authentication feature"
# Stash specific files
git stash push -m "API changes only" api/users.ts api/auth.ts
# Stash including untracked files
git stash push -u -m "Including new config files"
# Stash everything including ignored files
git stash push -a -m "Complete workspace state"
Stash Management:
# List stashes with details
git stash list
# stash@{0}: On main: WIP: user authentication
# stash@{1}: On feature-x: API changes only
# stash@{2}: On main: Including new config files
# View stash content without applying
git stash show stash@{0}
git stash show -p stash@{0} # With full diff
# Apply specific stash without removing it
git stash apply stash@{1}
# Apply and remove stash
git stash pop stash@{0}
# Create branch from stash
git stash branch new-feature-branch stash@{2}
Partial Stash (Patch Mode):
# Interactive stash - choose which changes to save
git stash push -p
# Git will ask for each hunk:
# y - stash this hunk
# n - do not stash this hunk
# q - quit; do not stash this or remaining hunks
# a - stash this and all remaining hunks
# d - do not stash this or remaining hunks
# s - split hunk into smaller parts
# e - edit hunk manually
Stash Workflow Patterns:
# Pattern 1: Quick context switch
alias gswitch='git stash push -u -m "Quick switch: $(git branch --show-current)"'
# Pattern 2: Stash untracked only
alias gstash-new='git stash push -u --keep-index -m "New files only"'
# Pattern 3: Stash by type of change
git stash push tests/**/*.test.ts -m "Test changes only"
git stash push src/**/*.css -m "Style changes only"
Stash Cleanup:
# Delete specific stash
git stash drop stash@{1}
# Delete all stashes
git stash clear
# Create backup before cleaning
git stash list > stash-backup.txt
6. Productive Git Aliases
Create custom commands that simplify complex workflows.
Navigation Aliases:
# View recent commits with pretty format
git config --global 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"
# Usage: git lg
# View modified files in last commit
git config --global alias.last "log -1 HEAD --stat"
# View branches sorted by modification date
git config --global alias.recent "branch --sort=-committerdate --format='%(HEAD) %(color:yellow)%(refname:short)%(color:reset) - %(color:red)%(objectname:short)%(color:reset) - %(contents:subject) - %(authorname) (%(color:green)%(committerdate:relative)%(color:reset))'"
Cleanup Aliases:
# Clean up merged branches
git config --global alias.cleanup "!git branch --merged | grep -v '\*\\|main\\|master\\|develop' | xargs -n 1 git branch -d"
# Remove locally deleted remote branches
git config --global alias.prune-remote "remote prune origin"
# Unstage all files
git config --global alias.unstage "reset HEAD --"
# Undo last commit keeping changes
git config --global alias.undo "reset --soft HEAD~1"
Information Aliases:
# View contributions by author
git config --global alias.contrib "shortlog --summary --numbered"
# View ignored files that are tracked
git config --global alias.ignored "ls-files --others --i --exclude-standard"
# Find commits that modified specific file
git config --global alias.find-file "log --all --full-history --"
# View who modified each line (improved blame)
git config --global alias.who "blame -w -C -C -C"
Workflow Aliases:
# Commit with message and push in one command
git config --global alias.cap "!git add -A && git commit -m"
# Usage: git cap "feat: new feature"
# Quick sync with remote
git config --global alias.sync "!git fetch origin && git rebase origin/main"
# Create feature branch
git config --global alias.feature "!f() { git checkout -b feature/$1; }; f"
# Usage: git feature user-auth
# Quick fix
git config --global alias.fix "!f() { git checkout -b fix/$1; }; f"
# Usage: git fix typo-in-docs
# Publish branch
git config --global alias.publish "!git push -u origin $(git branch --show-current)"
Aliases with Complex Scripts:
# Configure in ~/.gitconfig:
[alias]
# Squash all commits since branch point
squash-all = "!f() {
git reset --soft $(git merge-base HEAD ${1-main}) &&
git commit --edit -m\"$(git log --format=%B --reverse HEAD..HEAD@{1})\";
}; f"
# Archive branch (tag and delete)
archive = "!f() {
git tag archive/$1 $1 &&
git branch -D $1;
}; f"
# Repository stats
stats = "!git log --shortstat --author=\"$(git config user.name)\"
| grep -E 'files? changed'
| awk '{files+=$1; inserted+=$4; deleted+=$6}
END {print \"Files changed:\", files,
\"Lines inserted:\", inserted, \"Lines deleted:\", deleted}'"
7. Conventional Commits with Automation
Maintain a semantic commit history that enables automatic changelogs.
Conventional Commits Format:
# Basic structure
<type>[optional scope]: <description>
[optional body]
[optional footer(s)]
# Standard types:
feat: New feature
fix: Bug fix
docs: Documentation changes
style: Formatting (does not affect code)
refactor: Refactoring
perf: Performance improvement
test: Tests
chore: Maintenance/config
# Breaking changes
feat!: description # ! indicates breaking change
feat(api)!: breaking change in API
BREAKING CHANGE: description in footer
Practical Examples:
# Feature with scope
git commit -m "feat(auth): add OAuth2 authentication"
# Fix with issue reference
git commit -m "fix(api): handle null values in user endpoint
Fixes #123"
# Performance improvement
git commit -m "perf(database): optimize query with indexes"
# Breaking change
git commit -m "feat(api)!: change response format to JSON API spec
BREAKING CHANGE: API responses now follow JSON API specification.
Clients need to update parsing logic."
# Multiple related changes
git commit -m "refactor(user): restructure user module
- Extract validation logic
- Improve error handling
- Add comprehensive tests
Relates to #456"
Commitizen Setup:
# Install Commitizen
npm install -g commitizen cz-conventional-changelog
# Configure in project
echo '{ "path": "cz-conventional-changelog" }' > ~/.czrc
# Use instead of git commit
git cz
# Commitizen guides interactively to create correct commit
Commitlint for Validation:
# Install commitlint
npm install --save-dev @commitlint/cli @commitlint/config-conventional
# Configure
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
# Integrate with Husky
npx husky add .husky/commit-msg 'npx --no -- commitlint --edit $1'
# Now poorly formatted commits will be rejected:
# ❌ git commit -m "Fixed bug"
# ✅ git commit -m "fix: resolve authentication issue"
Custom Configuration:javascript
// commitlint.config.js
module.exports = {
extends: [‘@commitlint/config-conventional’],
rules: {
‘type-enum’: [2, ‘always’, [
‘feat’, // New feature
‘fix’, // Bug fix
‘docs’, // Documentation
‘style’, // Formatting
‘refactor’, // Code refactoring
‘perf’, // Performance
‘test’, // Tests
‘build’, // Build system
‘ci’, // CI/CD
‘chore’, // Maintenance
‘revert’ // Revert commit
]],
‘scope-enum’: [2, ‘always’, [
‘api’,
‘ui’,
‘auth’,
‘database’,
‘config’
]],
‘subject-max-length’: [2, ‘always’, 72],
‘body-max-line-length’: [2, ‘always’, 100]
}
};
Generate Automatic Changelog:
# Install standard-version
npm install --save-dev standard-version
# Add script to package.json
{
"scripts": {
"release": "standard-version"
}
}
# Generate release and changelog
npm run release
# Generates:
# - Bump version in package.json
# - Automatically updates CHANGELOG.md
# - Creates git tag
# - Generates release commit
# Release types:
npm run release -- --release-as minor # 1.0.0 → 1.1.0
npm run release -- --release-as major # 1.1.0 → 2.0.0
npm run release -- --release-as patch # 1.1.0 → 1.1.1
Automatically Generated CHANGELOG:
# Changelog
## [2.1.0] - 2025-01-15
### Features
- **auth**: add OAuth2 authentication ([a1b2c3d](link))
- **api**: implement rate limiting ([d4e5f6g](link))
### Bug Fixes
- **api**: handle null values in user endpoint ([h7i8j9k](link))
- **ui**: fix responsive layout on mobile ([k0l1m2n](link))
### Performance Improvements
- **database**: optimize queries with indexes ([n3o4p5q](link))
### BREAKING CHANGES
- **api**: change response format to JSON API spec
Comprehensive Setup: Full Git Config
Optimized configuration file for maximum productivity:
# ~/.gitconfig
[user]
name = Your Name
email = your.email@example.com
[core]
editor = code --wait
autocrlf = input
excludesfile = ~/.gitignore_global
pager = delta # better diff viewer
[pull]
rebase = true # rebase by default when pulling
[push]
default = current
autoSetupRemote = true # auto-setup of tracking
[fetch]
prune = true # automatically clean up refs
[rebase]
autosquash = true
autoStash = true
[merge]
conflictstyle = diff3 # better conflict resolution
tool = vscode
[diff]
algorithm = histogram # better diff algorithm
colorMoved = zebra
[blame]
coloring = highlightRecent
[help]
autocorrect = 10 # auto-correct typos after 1 second
[alias]
# (Include all aliases from tip #6)
Globally Ignore Files:
# ~/.gitignore_global
# OS
.DS_Store
Thumbs.db
# IDEs
.vscode/
.idea/
*.swp
*.swo
# Dependencies
node_modules/
vendor/
# Build outputs
dist/
build/
*.log
# Environment
.env
.env.local
Weekly Implementation Plan
Week 1: Fundamentals
- Set up git config with basic aliases
- Practice interactive rebase in feature branch
- Set up commitlint with Husky
Week 2: Advanced Workflows
- Create worktree for code review
- Use git bisect to find regression
- Set up custom stash workflow
Week 3: Automation
- Set up Commitizen for conventional commits
- Configure standard-version for changelogs
- Create custom aliases for your workflow
Week 4: Optimization
- Document workflows for the team
- Refine git hooks according to needs
- Establish branch and commit conventions
Success Metrics
Before implementing advanced techniques:
- Time spent on conflict resolution: ~30min average
- Commits per feature: ~15 messy commits
- Time to recovery from errors: ~45min
- Changelog maintenance: Manual and outdated
After implementing:
- Time spent on conflict resolution: ~10min average (-67%)
- Commits per feature: 3-5 organized commits (-70%)
- Time to recovery: ~5min with reflog (-89%)
- Changelog: Automatic and up-to-date
Weekly Challenge
Implement at least 3 techniques this week:
- Interactive Rebase: Clean up the history of your current feature branch
- Git Worktrees: Create a worktree for code review without losing context
- Conventional Commits: Set up commitlint and use standard format
- Git Aliases: Create 5 aliases you use in your daily workflow
- Git Bisect: Use bisect the next time you find a regression
Bonus: Set up automated changelog generation and share your first automatically generated CHANGELOG.md.
Git is much more than commit and push. Mastering these advanced techniques transforms Git from a basic version control tool into a powerful system that optimizes collaboration, debugging, and code quality.
Which of these techniques do you already use in your workflows? What Git commands do you consider indispensable that few know about?
techtipsmonday git versioncontrol productivity workflow devtools bestpractices