Troubleshooting

Common Git Mistakes

Learn to identify, avoid, and fix the most common Git mistakes developers encounter, with practical solutions and prevention strategies

Common Git Mistakes: Prevention and Solutions

Even experienced developers make Git mistakes. This comprehensive guide covers the most common Git errors, their causes, immediate fixes, and prevention strategies to help you become a more confident Git user.

Wrong Commit Message

One of the most frequent mistakes is committing with an incorrect or incomplete message.

The Problem

# Oops! Wrong commit message
git commit -m "fix bug"  # Too vague
git commit -m "asdf"     # Meaningless
git commit -m "Fixed the login issue on the user dashboard when users try to sign in with invalid credentials and the error message was not showing properly"  # Too long

The Solution

# Fix the last commit message
git commit --amend -m "fix(auth): display proper error message for invalid login credentials"

# If you've already pushed (and it's safe to force push)
git push --force-with-lease origin feature-branch

# For multiple commits, use interactive rebase
git rebase -i HEAD~3  # Edit last 3 commits
# Change 'pick' to 'reword' for commits you want to edit

Prevention Strategies

# Set up commit message template
git config --global commit.template ~/.gitmessage

# ~/.gitmessage template
# <type>(<scope>): <subject>
# 
# <body>
# 
# <footer>

# Use conventional commits
# feat: add new feature
# fix: fix a bug  
# docs: update documentation
# style: formatting changes
# refactor: code restructuring
# test: add tests
# chore: maintenance tasks

# Commit message validation hook
#!/bin/sh
# .git/hooks/commit-msg
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
    echo "Invalid commit message format!"
    echo "Use: type(scope): description"
    exit 1
fi

Committing Too Much or Too Little

Developers often commit entire features in one go or make commits too granular.

The Problem

# Too much in one commit
git add .
git commit -m "Implement user authentication system"
# This includes login, logout, password reset, user registration, session management...

# Too granular
git commit -m "Add opening brace"
git commit -m "Add closing brace"  
git commit -m "Fix indentation"

The Solution

# Split large commit using reset
git reset HEAD~1  # Undo last commit, keep changes
git add auth/login.js
git commit -m "feat(auth): implement user login functionality"
git add auth/register.js  
git commit -m "feat(auth): implement user registration"

# Combine small commits using interactive rebase
git rebase -i HEAD~5
# Change 'pick' to 'squash' or 's' for commits to combine

Best Practices

# Use staging area strategically
git add -p  # Interactive staging - stage parts of files
git add file1.js file2.js  # Stage specific files
git commit -m "feat: implement user login validation"

# Atomic commits: one logical change per commit
# ✅ Good commit structure:
# - Each commit compiles and tests pass
# - Related changes grouped together
# - Clear, descriptive commit messages
# - Easy to review and revert if needed

Working on Wrong Branch

Accidentally working on main/master or wrong feature branch.

The Problem

# Made changes on main branch instead of feature branch
git branch  # Shows: * main
# You've been coding for hours on main!

The Solution

# If changes are not committed yet
git stash  # Save current work
git checkout -b feature/my-feature  # Create correct branch
git stash pop  # Restore work to new branch

# If already committed to wrong branch
git branch feature/my-feature  # Create new branch from current position
git reset --hard HEAD~2  # Reset main back 2 commits (adjust number)
git checkout feature/my-feature  # Switch to feature branch

# Alternative: Use cherry-pick
git checkout main
git log --oneline -5  # Find commit hashes to move
git reset --hard HEAD~2  # Reset main
git checkout -b feature/my-feature  
git cherry-pick <commit-hash-1> <commit-hash-2>  # Move commits

Prevention Strategies

# Always check current branch
git branch  # or git status
git branch --show-current  # Show only current branch

# Set up shell prompt to show branch
# Add to ~/.bashrc or ~/.zshrc
parse_git_branch() {
    git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/(\1)/'
}
export PS1="\u@\h \[\033[32m\]\w\[\033[33m\]\$(parse_git_branch)\[\033[00m\] $ "

# Use Git aliases for safer workflows
git config --global alias.feature '!f() { git checkout main && git pull && git checkout -b feature/$1; }; f'
git config --global alias.switch-safe '!f() { git stash && git checkout $1 && git stash pop; }; f'

# Pre-commit hook to prevent commits to protected branches
#!/bin/sh
# .git/hooks/pre-commit
branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$branch" = "main" ] || [ "$branch" = "master" ]; then
  echo "You can't commit directly to $branch branch"
  exit 1
fi

Merge Conflicts

Incorrectly resolving or avoiding merge conflicts.

Understanding Merge Conflicts

# Conflict markers explanation
<<<<<<< HEAD (current change - your branch)
function login(username, password) {
    return authenticate(username, password);
}
=======
function login(user, pass) {
    return auth.verify(user, pass);  
}
>>>>>>> feature-branch (incoming change)

# What each part means:
# <<<<<<< HEAD: Start of your current branch changes
# =======: Separator between changes  
# >>>>>>> branch: End of incoming branch changes

The Solution

# Proper conflict resolution workflow
git merge feature-branch
# Conflict occurs

# 1. Identify conflicted files
git status
# Shows: both modified: file.js

# 2. Open file and resolve conflicts manually
# Remove conflict markers and choose/combine changes

# 3. Mark as resolved
git add file.js

# 4. Complete the merge
git commit  # Don't use -m, let Git generate merge commit message

# Using merge tools
git config --global merge.tool vimdiff  # or meld, kdiff3, etc.
git mergetool  # Open visual merge tool

Prevention and Best Practices

# Keep branches up to date
git checkout feature-branch
git rebase main  # Reapply feature changes on top of main

# Regular syncing workflow
git checkout main
git pull origin main
git checkout feature-branch  
git rebase main
# Resolve conflicts incrementally, commit by commit

# Use merge strategies
git merge -X ours feature-branch    # Favor your changes in conflicts
git merge -X theirs feature-branch  # Favor their changes in conflicts

# Abort problematic merges
git merge --abort  # Cancel merge and return to pre-merge state
git rebase --abort # Cancel rebase and return to pre-rebase state

Remote Repository Mistakes

Force Push to Shared Branch

Force pushing to branches that others are working on.

The Problem

# Dangerous force push
git push --force origin main  # Overwrites others' work!
git push -f origin shared-feature  # Could lose teammates' commits

The Solution

# If you just force pushed and realized the mistake:
# 1. Immediately contact your team
# 2. Check if anyone else pushed after your force push
git reflog origin/main  # See what was overwritten

# 3. Restore previous state if possible
git push origin <previous-commit-hash>:main --force

# Better approach: Use force-with-lease
git push --force-with-lease origin feature-branch
# Only force pushes if remote hasn't changed since your last fetch

Prevention Strategies

# Set up branch protection rules (GitHub/GitLab)
# - Require pull request reviews
# - Disable force pushes to main/master
# - Require status checks to pass

# Use safer force push alias
git config --global alias.force-safe 'push --force-with-lease'

# Pre-push hook to prevent force pushes to main
#!/bin/sh
# .git/hooks/pre-push
protected_branch='main'
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
if [ $protected_branch = $current_branch ]; then
    echo "Direct push to main branch is not allowed"
    exit 1
fi

# Team workflow: Always use pull requests
# Never push directly to main/master
# Always create feature branches

Wrong Remote URL

Pushing to the wrong repository or using incorrect URL.

The Problem

# Pushing to fork instead of upstream
git remote -v
# origin  https://github.com/yourname/project.git (fetch)
# origin  https://github.com/yourname/project.git (push)
# Should be pushing to organization repository!

# Or using HTTPS instead of SSH (authentication issues)

The Solution

# Fix remote URL
git remote set-url origin https://github.com/organization/project.git

# Add upstream remote for forks
git remote add upstream https://github.com/original-owner/project.git

# Switch from HTTPS to SSH
git remote set-url origin git@github.com:username/repository.git

# Verify remotes
git remote -v
git remote show origin  # Detailed remote information

Best Practices

# Check remotes when cloning forks
git clone https://github.com/yourname/project.git
cd project
git remote add upstream https://github.com/original/project.git

# Sync with upstream regularly
git fetch upstream
git checkout main
git merge upstream/main
git push origin main

# Use SSH keys for authentication
ssh-keygen -t ed25519 -C "your.email@example.com"
# Add public key to GitHub/GitLab
ssh -T git@github.com  # Test connection

File and Data Mistakes

Accidentally Deleting Files

Losing files through incorrect Git commands.

The Problem

# Accidentally deleted important files
git rm important-file.txt
git commit -m "cleanup"
# File is gone from working directory and Git history!

# Or used wrong reset
git reset --hard HEAD~5  # Lost 5 commits of work!

The Solution

# Recover deleted file from Git history
git log --oneline --follow -- important-file.txt  # Find when it was deleted
git checkout <commit-hash>~1 -- important-file.txt  # Restore from before deletion
git add important-file.txt
git commit -m "restore accidentally deleted file"

# Recover from hard reset using reflog
git reflog  # Shows all ref changes
# Find the commit hash before the reset
git reset --hard <commit-hash>  # Restore to that point

# If file was never committed but in working directory
# Check if it's in stash
git stash list
git stash show -p stash@{0}  # Preview stash contents
git stash pop stash@{0}  # Restore from stash

Prevention Strategies

# Use safer alternatives to destructive commands
git reset --soft HEAD~1    # Instead of --hard (keeps changes staged)
git reset --mixed HEAD~1   # Default (keeps changes in working dir)

# Always check what will be deleted
git rm --dry-run *.txt  # Preview what would be removed
git clean -n            # Preview what would be cleaned

# Create aliases for safer operations
git config --global alias.unstage 'reset HEAD --'
git config --global alias.undo 'reset --soft HEAD~1'
git config --global alias.safe-clean 'clean -n'  # Always dry-run first

# Regular backups
git config --global alias.backup '!git bundle create ../backup-$(date +%Y%m%d).bundle --all'

Committing Sensitive Information

Accidentally committing passwords, API keys, or private data.

The Problem

# Committed secrets
git add .
git commit -m "update config"
git push origin main
# config.js contains API keys!

# Or committed large binary files
git add data/large-file.bin  # 100MB file
git commit -m "add dataset"
# Repository becomes huge and slow

The Solution

# If not yet pushed - amend or reset
git reset HEAD~1  # Remove commit, keep changes
# Edit files to remove secrets
git add . && git commit -m "update config (removed secrets)"

# If already pushed - remove from history
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch config.js' \
--prune-empty --tag-name-filter cat -- --all

# Or use BFG Repo-Cleaner (easier)
java -jar bfg.jar --delete-files config.js
git reflog expire --expire=now --all
git gc --prune=now --aggressive
git push --force-with-lease origin --all

# For secrets specifically
git filter-branch --force --index-filter \
'git rm --cached --ignore-unmatch *.key *.pem *.p12' \
--prune-empty --tag-name-filter cat -- --all

# Change all exposed secrets immediately!
# Revoke API keys, change passwords, rotate certificates

Prevention Strategies

# Use .gitignore effectively
# .gitignore
*.env
*.key
*.pem
config/secrets.js
.aws/
.ssh/
node_modules/
*.log
dist/
build/

# Environment variables instead of hardcoded secrets
# config.js
const config = {
    apiKey: process.env.API_KEY,
    dbPassword: process.env.DB_PASSWORD
};

# Pre-commit hook to detect secrets
#!/bin/sh
# .git/hooks/pre-commit
git diff --cached --name-only | xargs grep -l "password\|key\|secret\|token" && {
    echo "Possible secret detected in staged files!"
    echo "Review your changes before committing."
    exit 1
}

# Use git-secrets tool
git secrets --install
git secrets --register-aws  # Detect AWS keys
git secrets --scan  # Check for secrets

# Template for environment setup
# .env.example (commit this)
API_KEY=your_api_key_here
DATABASE_URL=your_db_url_here

# .env (don't commit this)
API_KEY=actual_secret_key
DATABASE_URL=postgresql://localhost/mydb

History and Rebase Mistakes

Rebasing Public Branches

Rebasing branches that others are working on.

The Problem

# Rebased a shared branch
git checkout feature-shared
git rebase main  # Changes commit hashes
git push --force origin feature-shared
# Other developers' local branches are now out of sync!

The Solution

# If you just rebased a shared branch:
# 1. Communicate immediately with team
# 2. Team members need to update their local copies:

# For team members:
git checkout feature-shared
git fetch origin
git reset --hard origin/feature-shared  # Discard local changes
# Or if they have local commits:
git rebase origin/feature-shared  # Reapply local commits on new base

Prevention and Best Practices

# Golden rule: Never rebase public/shared branches
# Only rebase feature branches that you own

# Safe rebase workflow:
git checkout feature-mine  # Personal feature branch
git rebase main           # Safe to rebase
git push --force-with-lease origin feature-mine  # Update remote

# Use merge for shared branches
git checkout shared-feature
git merge main  # Creates merge commit, preserves history

# Alternative: Create pull request instead of direct rebase
# Let the hosting service (GitHub/GitLab) handle the integration

Lost Commits After Rebase

Losing commits during interactive rebase or reset operations.

The Problem

# Interactive rebase gone wrong
git rebase -i HEAD~10
# Accidentally deleted lines or used wrong commands
# Commits are missing!

# Or hard reset too far back
git reset --hard HEAD~5
# Realized you needed those commits

The Solution

# Use reflog to find lost commits
git reflog  # Shows all ref changes
# Output like:
# abcd123 HEAD@{0}: rebase: fast-forward
# efgh456 HEAD@{1}: rebase: checkout main
# ijkl789 HEAD@{2}: commit: my important work

# Restore to before the problematic operation
git reset --hard HEAD@{2}  # Go back to before rebase

# Or cherry-pick specific commits
git cherry-pick ijkl789  # Restore specific commit

# Create a recovery branch first
git branch recovery-branch HEAD@{2}  # Save state before reset
git checkout recovery-branch  # Examine what was lost

Prevention Strategies

# Always create backup branch before dangerous operations
git branch backup-before-rebase  # Create backup
git rebase -i HEAD~5              # Do dangerous operation
git branch -d backup-before-rebase  # Delete backup if successful

# Use reflog more frequently
git config --global core.logAllRefUpdates true  # Enable reflog
git config --global gc.reflogExpire 90.days     # Keep reflog longer

# Practice interactive rebase safely
git config --global alias.rb-safe '!f() { git branch backup-rebase-$(date +%s) && git rebase -i $1; }; f'

# Use autostash for cleaner rebases
git config --global rebase.autoStash true

Workflow and Collaboration Mistakes

Not Pulling Before Pushing

Trying to push without incorporating remote changes.

The Problem

git push origin main
# Error: Updates were rejected because the remote contains work
# that you do not have locally

The Solution

# Pull and handle the integration
git pull origin main  # Fetch and merge
# Or prefer rebase to maintain linear history
git pull --rebase origin main

# If merge conflicts occur during pull
git status  # See conflicted files
# Resolve conflicts manually
git add resolved-file.js
git commit  # Complete merge
# Or for rebase:
git add resolved-file.js  
git rebase --continue

# Then push
git push origin main

Best Practices

# Always pull before starting work
git checkout main
git pull origin main
git checkout -b feature/new-work

# Regular syncing during long-running branches
git checkout feature/long-running
git fetch origin
git rebase origin/main  # Stay up to date

# Use pull request workflow to avoid direct pushes
# This eliminates the problem entirely

# Set up automatic rebase on pull
git config --global pull.rebase true

Ignoring .gitignore

Adding files that should be ignored or not ignoring necessary files.

The Problem

# Committed files that should be ignored
git add .
git commit -m "update"
# Included node_modules/, .env, build artifacts!

# Or .gitignore not working because files already tracked
echo "*.log" >> .gitignore
git add .gitignore
git commit -m "ignore logs"
# But existing .log files still tracked!

The Solution

# Remove already-tracked files from Git (keep locally)
git rm --cached file-to-ignore.log
git rm --cached -r node_modules/
git commit -m "untrack ignored files"

# Then add to .gitignore
echo "file-to-ignore.log" >> .gitignore
echo "node_modules/" >> .gitignore
git add .gitignore
git commit -m "update gitignore"

# For files that shouldn't exist locally either
git rm file-and-delete.log  # Remove from Git and filesystem

Prevention and Best Practices

# Comprehensive .gitignore templates
# Use gitignore.io for language/framework specific templates
curl https://www.toptal.com/developers/gitignore/api/node,python,macos > .gitignore

# Common .gitignore patterns
# Dependencies
node_modules/
venv/
vendor/

# Build outputs  
dist/
build/
*.o
*.exe

# Environment and secrets
.env
.env.local
*.key
*.pem

# IDE files
.vscode/
.idea/
*.swp
*.swo

# OS generated
.DS_Store
Thumbs.db
*.tmp

# Check what would be added before committing
git add --dry-run .  # See what would be added
git status --ignored  # See ignored files

# Global gitignore for OS/IDE files
git config --global core.excludesfile ~/.gitignore_global
# ~/.gitignore_global
.DS_Store
.vscode/
*.swp

Performance and Maintenance Mistakes

Repository Getting Too Large

Not managing repository size properly.

The Problem

# Repository becomes slow due to:
# - Large binary files in history
# - No garbage collection
# - Keeping all refs and objects indefinitely
git clone repository.git  # Takes forever
git status  # Slow response

The Solution

# Check repository size
git count-objects -vH  # Detailed size information
du -sh .git            # Size of .git directory

# Run garbage collection
git gc --aggressive --prune=now

# Remove large files from history
git filter-branch --tree-filter 'rm -rf large-folder' HEAD
# Or use BFG Repo-Cleaner
java -jar bfg.jar --strip-blobs-bigger-than 50M

# For ongoing large files, use Git LFS
git lfs install
git lfs track "*.psd"
git lfs track "*.zip"
git add .gitattributes
git commit -m "track large files with LFS"

Prevention Strategies

# Set up Git LFS for large files from the start
git lfs install
echo "*.zip filter=lfs diff=lfs merge=lfs -text" >> .gitattributes
echo "*.pdf filter=lfs diff=lfs merge=lfs -text" >> .gitattributes

# Regular maintenance
git config --global gc.auto 256  # More frequent garbage collection
git config --global pack.window 20  # Better compression

# Monitor repository size
git config --global alias.size 'count-objects -vH'
git config --global alias.cleanup 'gc --aggressive --prune=now'

# Use shallow clones for CI/CD
git clone --depth 1 repository.git  # Only latest commit

Recovery and Emergency Procedures

Emergency Recovery Commands

When things go very wrong, these commands can save the day.

# Nuclear option - start over (be very careful!)
git fetch origin
git reset --hard origin/main  # Lose all local changes!
git clean -fd  # Remove untracked files and directories

# Less destructive recovery
git stash  # Save current work
git fetch origin
git reset --hard origin/main
git stash pop  # Try to restore work (may have conflicts)

# Find and restore anything
git reflog --all  # See all refs changes
git fsck --lost-found  # Find dangling objects
ls .git/lost-found/other/  # See lost objects
git show <object-hash>  # Examine lost content

Creating Recovery Points

# Before dangerous operations
git config --global alias.snapshot '!f() { git add -A && git commit -m "SNAPSHOT: before $1"; }; f'
git snapshot "major refactoring"

# Automatic backup to different remote
git remote add backup /path/to/backup/repo
git config --global alias.backup-push 'push backup --all --tags'

# Tag important states
git tag -a stable-v1.0 -m "Stable version before changes"
git tag -a before-refactor -m "Working state before refactoring"

Prevention Strategies and Best Practices

Pre-commit Hooks Setup

Prevent mistakes before they happen.

#!/bin/sh
# .git/hooks/pre-commit - Comprehensive pre-commit check

set -e

echo "Running pre-commit checks..."

# Check for secrets
if git diff --cached --name-only | xargs grep -l "password\|secret\|key\|token" 2>/dev/null; then
    echo "❌ Potential secrets detected!"
    exit 1
fi

# Check for large files
large_files=$(git diff --cached --name-only | xargs ls -la 2>/dev/null | awk '$5 > 5000000 {print $9}')
if [ -n "$large_files" ]; then
    echo "❌ Large files detected (>5MB):"
    echo "$large_files"
    echo "Consider using Git LFS"
    exit 1
fi

# Check for debugging code
if git diff --cached | grep -E "console\.log|debugger|TODO|FIXME" >/dev/null; then
    echo "⚠️  Warning: Debugging code or TODOs found in staged files"
    read -p "Continue anyway? (y/N): " -n 1 -r
    echo
    if [[ ! $REPLY =~ ^[Yy]$ ]]; then
        exit 1
    fi
fi

# Run tests if they exist
if [ -f "package.json" ] && command -v npm >/dev/null; then
    echo "Running tests..."
    npm test
fi

echo "✅ Pre-commit checks passed!"

Daily Workflow Checklist

# Morning routine alias
git config --global alias.morning '!f() {
    echo "🌅 Starting daily Git routine..."
    echo "Current branch: $(git branch --show-current)"
    git status --short
    echo ""
    echo "Fetching latest changes..."
    git fetch --all --prune
    echo ""
    echo "Remote status:"
    git status --branch --short
    echo ""
    echo "Recent commits:"
    git log --oneline -5
    echo "✅ Ready to start work!"
}; f'

# End of day routine alias
git config --global alias.evening '!f() {
    echo "🌙 End of day Git routine..."
    echo "Current status:"
    git status
    echo ""
    if [ -n "$(git status --porcelain)" ]; then
        echo "Uncommitted changes detected."
        read -p "Stash changes? (y/N): " -n 1 -r
        echo
        if [[ $REPLY =~ ^[Yy]$ ]]; then
            git stash push -m "End of day stash $(date)"
        fi
    fi
    echo "✅ Good night!"
}; f'

Next Steps

🛠️ Congratulations! You're now equipped to handle common Git mistakes with confidence.

Continue strengthening your Git skills:

  1. Master Git Recovery Techniques - Deep dive into data recovery
  2. Optimize Git Performance - Solve speed and size issues
  3. Learn Advanced Git Workflows - Apply best practices in teams

Advanced Mistake Prevention

  • Set up comprehensive Git hooks for your team
  • Create automated backup and recovery systems
  • Develop Git workflow documentation for your organization
  • Build monitoring for repository health and size
  • Establish incident response procedures for Git emergencies
  • Train team members on Git best practices and recovery techniques

Emergency Contacts and Resources

# Create emergency recovery scripts
#!/bin/bash
# emergency-recovery.sh
echo "Git Emergency Recovery Kit"
echo "========================="
echo "1. Show current status"
echo "2. Show reflog (find lost commits)"
echo "3. Show all branches"
echo "4. Create recovery branch"
echo "5. Reset to safe state"
echo ""
read -p "Choose option: " option
case $option in
    1) git status -v;;
    2) git reflog --all;;
    3) git branch -a;;
    4) git branch recovery-$(date +%s);;
    5) git stash && git reset --hard origin/main;;
    *) echo "Invalid option";;
esac

Remember: Every mistake is a learning opportunity. The more mistakes you make (and fix), the better you become at Git!