Git Troubleshooting Guide
Solutions to common Git problems you'll encounter during daily development work.
Overview
This guide provides practical solutions to Git problems organized by scenario. Each solution includes step-by-step commands and explanations.
If you're unsure about a solution, especially one involving --force or reset --hard, ask a teammate first. It's better to ask than to lose work!
Merge Conflicts
Understanding Merge Conflicts
Merge conflicts occur when Git's three-way merge algorithm cannot automatically resolve differences. The algorithm compares three commits: the common ancestor (merge base), your current branch (HEAD), and the incoming branch. If both branches modified the same lines differently since the common ancestor, Git cannot determine which change to keep and marks the file with conflict markers. Git uses content-based merging at the line level, so even changes to different parts of the same line will conflict.
Conflict markers in files:
<<<<<<< HEAD (Current Change)
public void processPayment(Payment payment) {
validatePayment(payment);
// ... existing code ...
=======
public void processPayment(PaymentRequest payment) {
validateRequest(payment);
// ... incoming code ...
>>>>>>> feature/JIRA-1234 (Incoming Change)
}
Resolving Merge Conflicts During Pull
Scenario: Conflicts when pulling latest develop
$ git pull origin develop
Auto-merging src/services/PaymentService.java
CONFLICT (content): Merge conflict in src/services/PaymentService.java
Automatic merge failed; fix conflicts and then commit the result.
Solution:
# 1. Check which files have conflicts
git status
# Output shows: "both modified: src/services/PaymentService.java"
# 2. Open conflicted file in editor
# Look for conflict markers: <<<<<<<, =======, >>>>>>>
# 3. Resolve conflicts manually
# Choose HEAD (your changes), incoming changes, or combine both
# Remove conflict markers
# 4. After fixing all conflicts, stage resolved files
git add src/services/PaymentService.java
# 5. Complete the merge
git commit -m "Merge branch 'develop' into feature/JIRA-1234
Resolved conflicts in PaymentService:
- Kept new validation logic from both branches
- Combined payment processing improvements"
# 6. Push the merge
git push origin feature/JIRA-1234-add-payment-retry
Resolving Conflicts During Rebase
Scenario: Conflicts when rebasing on develop
$ git rebase develop
CONFLICT (content): Merge conflict in src/services/PaymentService.java
error: could not apply abc1234... feat(payments): add retry logic
Solution:
# 1. Check current status
git status
# Shows current rebase state and conflicted files
# 2. Resolve conflicts in the file (same as above)
# 3. Stage resolved files
git add src/services/PaymentService.java
# 4. Continue rebase (NOT git commit!)
git rebase --continue
# 5. If more conflicts, repeat steps 2-4
# If all resolved, rebase completes automatically
# 6. Force push (your feature branch only!)
git push --force-with-lease origin feature/JIRA-1234-add-payment-retry
Abort rebase if needed:
# Cancel rebase and return to state before rebase started
git rebase --abort
Accepting All Changes from One Side
Take all changes from develop (theirs):
# During merge
git checkout --theirs src/services/PaymentService.java
git add src/services/PaymentService.java
# During rebase
git checkout --theirs src/services/PaymentService.java
git add src/services/PaymentService.java
git rebase --continue
Keep all your changes (ours):
# During merge
git checkout --ours src/services/PaymentService.java
git add src/services/PaymentService.java
# During rebase
git checkout --ours src/services/PaymentService.java
git add src/services/PaymentService.java
git rebase --continue
Using Merge Tool
# Configure merge tool (one-time setup)
git config --global merge.tool vimdiff
# Or: meld, kdiff3, p4merge, etc.
# Launch merge tool for conflicts
git mergetool
# After resolving, clean up backup files
rm *.orig
# Complete merge/rebase
git commit # for merge
git rebase --continue # for rebase
Conflict Resolution Decision Tree
Committed to Wrong Branch
Scenario 1: Committed to develop Instead of Feature Branch
Problem:
$ git branch
* develop # Oh no! Should be on feature branch
$ git log --oneline -1
abc1234 feat(payments): add new feature # This commit shouldn't be here
Solution (if NOT yet pushed):
# 1. Create feature branch from current state
git checkout -b feature/JIRA-1234-add-feature
# 2. Go back to develop
git checkout develop
# 3. Remove commit from develop (moves HEAD back one commit)
git reset --hard HEAD~1
# 4. Verify develop is clean
git log --oneline -1
# 5. Return to feature branch (has your commit)
git checkout feature/JIRA-1234-add-feature
# 6. Push feature branch
git push -u origin feature/JIRA-1234-add-feature
Solution (if ALREADY pushed to develop):
# 1. Create feature branch with the commit
git checkout -b feature/JIRA-1234-add-feature
# 2. Push feature branch
git push -u origin feature/JIRA-1234-add-feature
# 3. Go back to develop and revert
git checkout develop
git revert HEAD
git push origin develop
# Note: This creates a revert commit in develop
# Your actual feature will be merged properly via MR later
Scenario 2: Committed to Wrong Feature Branch
Solution using cherry-pick:
# 1. Note the commit hash
git log --oneline -1
# Output: abc1234 feat(payments): add validation
# 2. Switch to correct branch
git checkout feature/JIRA-1234-correct-feature
# 3. Cherry-pick the commit
git cherry-pick abc1234
# 4. Go back to wrong branch
git checkout feature/JIRA-9999-wrong-feature
# 5. Remove the commit
git reset --hard HEAD~1
# 6. Push both branches
git checkout feature/JIRA-1234-correct-feature
git push origin feature/JIRA-1234-correct-feature
git checkout feature/JIRA-9999-wrong-feature
git push --force-with-lease origin feature/JIRA-9999-wrong-feature
Undoing Commits
Undo Last Commit (Not Yet Pushed)
Keep changes in working directory:
# Undo commit, keep changes unstaged
git reset HEAD~1
# Or keep changes staged
git reset --soft HEAD~1
# Make corrections, then recommit
git add .
git commit -m "feat(payments): corrected implementation"
Discard changes completely:
# DANGEROUS: Permanently deletes changes
git reset --hard HEAD~1
Undo Multiple Commits
# Undo last 3 commits, keep changes
git reset HEAD~3
# Undo to specific commit
git reset <commit-hash>
# Example: Reset to state before feature work
git reset abc1234
Undo Commit After Pushing
Use revert (creates new commit):
# Revert last commit
git revert HEAD
# Revert specific commit
git revert abc1234
# Push the revert
git push origin feature/JIRA-1234-add-payment-retry
Example revert commit message:
git revert abc1234 -m "revert: undo payment retry feature
Production issues with external gateway timeout.
Will reintroduce after configuration fixes.
Related to JIRA-1234"
Understanding the fundamental difference between reset and revert is critical for safe Git operations:
-
Reset: Moves the branch pointer backward, effectively "deleting" commits from history. Technically, the commits still exist temporarily (accessible via reflog) but are orphaned - no branch references them. Git's garbage collector will eventually permanently delete these unreachable commits (after ~90 days). Use only on unpushed commits to avoid desynchronizing team members' local copies.
-
Revert: Creates a new commit with inverse changes that undo a previous commit. The original commit remains in history unchanged. This preserves the complete audit trail, which is essential for compliance and understanding how bugs were introduced and fixed. Safe for pushed commits because it adds history rather than rewriting it.
Never reset commits that others might have pulled! If teammate Alice has commits A-B-C and you reset to remove C, Alice's next push will re-introduce C, creating confusion. Reset forces everyone to reset their copies or create divergent histories.
Rebasing Issues
Branch Has Diverged After Rebase
Problem:
$ git push origin feature/JIRA-1234
! [rejected] feature/JIRA-1234 -> feature/JIRA-1234 (non-fast-forward)
error: failed to push some refs
Solution:
# Force push with safety check
git push --force-with-lease origin feature/JIRA-1234
# If force-with-lease fails (someone else pushed):
# 1. Pull their changes first
git pull origin feature/JIRA-1234
# 2. Then push
git push origin feature/JIRA-1234
Lost Commits After Rebase
Solution using reflog:
# 1. View reflog to find lost commit
git reflog
# Output shows:
# abc1234 HEAD@{0}: rebase finished
# def5678 HEAD@{1}: commit: feat(payments): lost feature
# ...
# 2. Create new branch from lost commit
git checkout -b feature/JIRA-1234-recovered def5678
# Or cherry-pick specific commit
git checkout feature/JIRA-1234
git cherry-pick def5678
Rebase Keeps Showing Same Conflict
Problem: Conflict appears for multiple commits during rebase
Solution:
# Option 1: Resolve conflict for each commit
# (tedious if conflict appears many times)
# Option 2: Abort and use merge instead
git rebase --abort
git merge develop
# Option 3: Squash commits first, then rebase
git rebase -i HEAD~5 # Interactive rebase
# Mark commits as 'squash' to combine them
# Then rebase on develop
git rebase develop
Branch Management Issues
Can't Delete Branch
Problem:
$ git branch -d feature/JIRA-1234
error: The branch 'feature/JIRA-1234' is not fully merged.
Solution:
# Check if branch is actually merged
git branch --merged develop | grep feature/JIRA-1234
# If merged but Git doesn't recognize it (after rebase):
git branch -D feature/JIRA-1234 # Force delete
# If not merged and you want to keep it:
# Don't delete! Push to remote first
git push origin feature/JIRA-1234
Accidentally Deleted Branch
Solution using reflog:
# 1. Find the deleted branch in reflog
git reflog | grep "feature/JIRA-1234"
# Output: abc1234 HEAD@{5}: checkout: moving from feature/JIRA-1234 to develop
# 2. Recreate branch at that commit
git checkout -b feature/JIRA-1234 abc1234
# 3. Push to remote
git push -u origin feature/JIRA-1234
Can't Switch Branches (Uncommitted Changes)
Problem:
$ git checkout develop
error: Your local changes to the following files would be overwritten by checkout:
src/services/PaymentService.java
Please commit your changes or stash them before you switch branches.
Solution 1: Stash changes:
git stash save "WIP: payment validation"
git checkout develop
# ... do work on develop ...
git checkout feature/JIRA-1234
git stash pop
Solution 2: Commit changes:
git add .
git commit -m "WIP: payment validation (incomplete)"
git checkout develop
Solution 3: Force checkout (DANGEROUS - loses changes):
git checkout -f develop
Remote Repository Issues
Push Rejected (Non-Fast-Forward)
Problem:
$ git push origin develop
! [rejected] develop -> develop (non-fast-forward)
Solution:
# 1. Pull latest changes
git pull origin develop
# 2. Resolve any conflicts if they occur
# 3. Push again
git push origin develop
Can't Pull (Uncommitted Changes)
Problem:
$ git pull origin develop
error: Your local changes to the following files would be overwritten by merge:
src/services/PaymentService.java
Solution:
# Option 1: Stash, pull, pop
git stash
git pull origin develop
git stash pop
# Option 2: Commit first
git add .
git commit -m "WIP: save before pull"
git pull origin develop
Remote Branch Deleted But Still Shows Locally
Problem:
$ git branch -r
# Shows: origin/feature/JIRA-1234 (but it's deleted on GitLab)
Solution:
# Prune remote tracking branches
git fetch --prune origin
# Or set to always prune
git config --global fetch.prune true
Wrong Remote URL
Problem: Pushing to wrong repository
Solution:
# Check current remote
git remote -v
# Update remote URL
git remote set-url origin [email protected]:company/correct-repo.git
# Verify
git remote -v
File and Change Issues
Accidentally Added Files
Before commit:
# Unstage specific file
git reset HEAD src/config/secrets.properties
# Unstage all files
git reset HEAD
After commit but before push:
# Remove file from commit but keep in working directory
git rm --cached src/config/secrets.properties
git commit --amend --no-edit
After pushing (sensitive data!):
# URGENT: Remove sensitive data from history
# Use BFG Repo-Cleaner or git-filter-repo
# Quick fix for recent commit:
git rm src/config/secrets.properties
git commit -m "fix: remove accidentally committed secrets"
git push origin feature/JIRA-1234
# Then rotate/invalidate the exposed secrets immediately!
If you accidentally commit secrets (passwords, API keys, tokens), act immediately - Git commits are immutable and distributed:
-
Remove from Git history immediately: Use
git filter-repoor BFG Repo-Cleaner to rewrite all commits that touched the secret file. Simplegit rmis insufficient because the secret remains in commit history accessible viagit log -porgit show <old-commit>. -
Rotate/invalidate the secrets: Even after removal from Git, assume the secret was compromised. If pushed to GitLab, the secret existed in the remote repository (potentially cached, backed up, or accessed by automated scanners). Immediately regenerate the secret in the source system (database passwords, API keys, etc.).
-
Notify security team: Security must assess if logs, backups, or monitoring systems captured the exposed secret. They can also check for unauthorized access attempts using the leaked credential.
-
Update secret management practices: Implement pre-commit hooks to scan for secrets (tools like
git-secretsortrufflehog), use environment variables instead of hardcoded secrets, and adopt secret management tools (Vault, AWS Secrets Manager).
Assume compromised secrets are already exposed! Git history is public once pushed, and automated bots scan public repositories for secrets within minutes of commit.
Committed Wrong Files
Solution:
# Undo commit, keep changes
git reset --soft HEAD~1
# Unstage wrong files
git reset HEAD wrong-file.txt
# Commit correct files
git add correct-file.txt
git commit -m "feat(payments): add validation"
# Handle wrong files
git stash # or git checkout -- wrong-file.txt
Case Sensitivity Issues (Renamed Files)
Problem: Renamed PaymentService.java → paymentService.java but Git doesn't detect
Solution:
# Force Git to recognize case change
git mv --force PaymentService.java paymentService.java
git commit -m "refactor: rename PaymentService file to fix case"
History and Log Issues
Find When Bug Was Introduced
Git bisect performs binary search through commit history to identify which commit introduced a bug. Instead of testing every commit linearly (O(n) complexity), bisect divides the commit range in half with each test, achieving O(log n) complexity. For example, finding the culprit in 1000 commits requires only ~10 tests instead of potentially 500 tests with linear search. Bisect maintains a range of "known good" and "known bad" commits, checking out the midpoint commit for testing, then narrowing the range based on your good/bad feedback.
Use git bisect (binary search):
# 1. Start bisect
git bisect start
# 2. Mark current commit as bad
git bisect bad
# 3. Mark last known good commit
git bisect good v1.4.0 # or commit hash
# 4. Git checks out middle commit
# Test if bug exists:
# - If bug exists: git bisect bad
# - If no bug: git bisect good
# 5. Repeat until Git finds the culprit commit
# Output: "abc1234 is the first bad commit"
# 6. End bisect
git bisect reset
# 7. Investigate the bad commit
git show abc1234
Automated bisect:
# Use script to automatically test each commit
git bisect start HEAD v1.4.0
git bisect run npm test # or any command that exits 0 if good
Find Who Changed a Line
# Show who last modified each line
git blame src/services/PaymentService.java
# Show changes for specific line range
git blame -L 50,75 src/services/PaymentService.java
# Ignore whitespace changes
git blame -w src/services/PaymentService.java
View File from Previous Commit
# View file as it was in specific commit
git show abc1234:src/services/PaymentService.java
# View file from previous commit
git show HEAD~1:src/services/PaymentService.java
# View file from specific tag
git show v1.4.0:src/services/PaymentService.java
Performance Issues
Slow Git Operations
Solution 1: Garbage collection:
# Clean up unnecessary files and optimize repository
git gc --aggressive --prune=now
Solution 2: Reduce repository size:
# Find large files in history
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sort -n -k 3 \
| tail -20
# Remove large files from history (use with caution!)
# Consider using BFG Repo-Cleaner
Solution 3: Shallow clone:
# Clone only recent history (faster for large repos)
git clone --depth 50 [email protected]:company/repo.git
# Later, fetch full history if needed
git fetch --unshallow
Large .git Folder
Solution:
# Check .git folder size
du -sh .git
# Clean up
git gc --aggressive --prune=now
git repack -ad
# Remove old reflog entries
git reflog expire --expire=30.days.ago --all
git gc --prune=now
Submodule Issues
Submodule Not Updated
Problem: Pulled repo but submodule still shows old commit
Solution:
# Initialize and update all submodules
git submodule update --init --recursive
# Or update specific submodule
git submodule update --init path/to/submodule
Changes in Submodule Not Showing
Solution:
# Update submodule to latest
cd path/to/submodule
git fetch origin
git checkout main
git pull origin main
# Go back to main repo
cd ../..
# Commit submodule update
git add path/to/submodule
git commit -m "chore: update submodule to latest"
git push origin feature/JIRA-1234
Configuration Issues
Wrong Author Name/Email
Fix for last commit:
# Change author of last commit
git commit --amend --author="Jane Doe <[email protected]>" --no-edit
git push --force-with-lease origin feature/JIRA-1234
Fix globally:
# Set correct author for future commits
git config --global user.name "Jane Doe"
git config --global user.email "[email protected]"
# Verify
git config --global user.name
git config --global user.email
Fix for multiple commits:
# Interactive rebase to change author
git rebase -i HEAD~3
# Mark commits as 'edit'
# For each commit:
git commit --amend --author="Jane Doe <[email protected]>" --no-edit
git rebase --continue
Line Ending Issues (Windows/Mac/Linux)
Configure globally:
# Windows
git config --global core.autocrlf true
# Mac/Linux
git config --global core.autocrlf input
# Force checkout with correct line endings
git rm --cached -r .
git reset --hard
Emergency Procedures
Completely Messed Up - Start Fresh
Solution:
# 1. Backup current work
cp -r project-dir project-dir-backup
# 2. Check what's committed vs uncommitted
git status
git diff > uncommitted-changes.patch
git stash
# 3. Delete local branch and recreate from remote
git fetch origin
git checkout develop
git branch -D feature/JIRA-1234
git checkout -b feature/JIRA-1234 origin/feature/JIRA-1234
# 4. Re-apply uncommitted changes if needed
git apply uncommitted-changes.patch
# or
git stash pop
Recover Lost Commits (Reflog)
The reflog (reference log) records every time HEAD or branch refs change in your local repository, independent of the commit graph. Unlike regular Git history which follows parent-child commit relationships, reflog is a chronological log of where HEAD has pointed. This includes commits that are no longer reachable from any branch (orphaned commits), making reflog essential for recovering "lost" work. Each reflog entry has a timestamp and is stored in .git/logs/refs/. Git garbage collection eventually removes reflog entries older than 90 days (configurable via gc.reflogExpire).
Reflog shows all actions on repository:
# View reflog
git reflog
# Output:
# abc1234 HEAD@{0}: commit: feat(payments): add validation
# def5678 HEAD@{1}: checkout: moving from develop to feature
# 1234567 HEAD@{2}: commit: fix(auth): token expiration
# ...
# Recover lost commit
git checkout -b recovery-branch abc1234
# Or cherry-pick
git cherry-pick abc1234
Reflog entries expire after 90 days by default. Don't rely on reflog for long-term recovery!
Repository Corrupted
Solution:
# 1. Check for corruption
git fsck --full
# 2. If errors found, try recovery
git fsck --full --no-dangling
# 3. If still corrupted, clone fresh and cherry-pick your work
cd ..
git clone [email protected]:company/repo.git repo-fresh
cd repo-fresh
git cherry-pick <your-commits>
Preventive Measures
Avoid Common Mistakes
Enable helpful warnings:
# Warn before pushing to main/develop
git config --global alias.push-check '!git rev-parse --abbrev-ref HEAD | grep -qE "^(main|develop)$" && echo "Warning: Pushing to protected branch!" || true && git push'
# Use push-check instead of push
git push-check origin feature-branch
Set up pre-commit hooks:
# .git/hooks/pre-commit
#!/bin/bash
# Prevent commits to main/develop
branch="$(git rev-parse --abbrev-ref HEAD)"
if [ "$branch" = "main" ] || [ "$branch" = "develop" ]; then
echo "ERROR: Direct commits to $branch are not allowed!"
echo "Create a feature branch instead."
exit 1
fi
# Check for debug statements
if git diff --cached | grep -E 'console\.log|debugger|TODO|FIXME'; then
echo "WARNING: Debug statements found in commit!"
read -p "Commit anyway? (y/n): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
exit 1
fi
fi
Enable commit signing:
# Configure GPG signing
git config --global user.signingkey <your-gpg-key>
git config --global commit.gpgsign true
Diagnostic Commands
Check Repository Health
# Verify repository integrity
git fsck --full
# Check for uncommitted changes
git status
# View recent actions
git reflog -10
# Check remote configuration
git remote -v
# List all branches and their tracking
git branch -vv
Debug Git Commands
# Show detailed output of Git command
GIT_TRACE=1 git pull origin develop
# Debug network issues
GIT_CURL_VERBOSE=1 git fetch origin
# Debug SSH authentication
GIT_SSH_COMMAND="ssh -vvv" git fetch origin
Getting Help
When to Ask for Assistance
Ask a teammate if:
- About to run
git push --forceon shared branch - Considering
git reset --hardon important commits - Dealing with sensitive data in Git history
- Multiple conflicts affecting many files
- Repository appears corrupted
- Unsure about impact of a command
Before Asking for Help
Provide context:
- What were you trying to do?
- What command did you run?
- What was the output/error?
- What is current state? (
git status,git log --oneline -5)
Example help request:
I was trying to rebase my feature branch on develop.
Command: git rebase develop
Error: CONFLICT (content): Merge conflict in PaymentService.java
Current state:
$ git status
rebase in progress; onto abc1234
You are currently rebasing branch 'feature/JIRA-1234' on 'abc1234'.
I'm not sure how to resolve the conflict without breaking things.
Can someone help?
Further Reading
- Git Daily Workflow - Common day-to-day Git commands
- GitFlow Branching Strategy - Overall branching model
- Pro Git Book - Chapter 7: Git Tools - Advanced Git techniques
External Resources
- Oh Shit, Git!?! - Plain English Git fixes
- Git Flight Rules - Community-sourced troubleshooting guide
- Atlassian Git Tutorials - Comprehensive Git guides
Summary
Key Takeaways:
- Merge conflicts: Resolve manually, test thoroughly before completing merge
- Wrong branch: Use cherry-pick to move commits; reset to remove from wrong branch
- Undo commits: Use reset for unpushed, revert for pushed commits
- Rebase issues: Abort with
git rebase --abortif stuck; use reflog to recover - Force push: Only on your own feature branches with
--force-with-lease - Lost commits: Use
git reflogto find and recover - Sensitive data: Remove immediately and rotate secrets
- Corruption: Verify with
git fsck; reclone if needed - Ask for help: When unsure, especially with destructive commands
- Preventive measures: Use pre-commit hooks and Git aliases