How to Fix Common Git Errors: Permission Denied, Push Rejected, Merge Conflicts
The Problem: Git Operations Failing
You're deploying code, pulling updates, or pushing commits and Git throws errors that block your workflow.
Common scenarios:
- Can't clone a repository
- Push rejected despite having commits
- In detached HEAD state and don't know how to recover
- Merge conflicts preventing deployment
This guide solves them all.
Error #1: Permission Denied (publickey)
Symptom
$ git clone git@github.com:user/repo.git
Cloning into 'repo'...
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
Root Cause
Git is trying to authenticate via SSH but:
- SSH key doesn't exist on your machine
- SSH key exists but not added to GitHub/GitLab
- SSH key exists but not loaded in SSH agent
Step 1: Check if SSH Key Exists
ls -la ~/.ssh/
Look for:
id_rsaandid_rsa.pub(RSA key)id_ed25519andid_ed25519.pub(Ed25519 key, recommended)
If missing, create one:
ssh-keygen -t ed25519 -C "your_email@example.com"
# Press Enter to accept defaults
Step 2: Add SSH Key to SSH Agent
# Start SSH agent
eval "$(ssh-agent -s)"
# Add key
ssh-add ~/.ssh/id_ed25519
Step 3: Add Public Key to GitHub/GitLab
Copy your public key:
cat ~/.ssh/id_ed25519.pub
Output:
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIAbcdef... your_email@example.com
GitHub:
- Go to Settings → SSH and GPG keys
- Click "New SSH key"
- Paste the public key
- Save
GitLab: Same process, but in GitLab settings.
Step 4: Test Connection
# GitHub
ssh -T git@github.com
# GitLab
ssh -T git@gitlab.com
Success output:
Hi username! You've successfully authenticated, but GitHub does not provide shell access.
Step 5: Clone Again
git clone git@github.com:user/repo.git
Alternative: Use HTTPS Instead
If SSH is problematic (firewalls, etc.), use HTTPS:
git clone https://github.com/user/repo.git
You'll be prompted for username/password (or personal access token).
Error #2: Git Push Rejected
Symptom
$ git push origin main
To github.com:user/repo.git
! [rejected] main -> main (fetch first)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the remote contains work that you do
hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: (e.g., 'git pull ...') before pushing again.
Root Cause
Someone else pushed commits to the remote branch after you pulled. Your local branch is behind.
Step 1: Check Status
git status
Output:
On branch main
Your branch and 'origin/main' have diverged,
and have 2 and 3 different commits each, respectively.
Step 2: Fetch Remote Changes
git fetch origin
This downloads commits from remote without merging.
Step 3: Check What You'd Be Merging
git log HEAD..origin/main --oneline
This shows commits on remote that you don't have locally.
Step 4: Merge or Rebase
Option A: Merge (Preserves history, creates merge commit)
git pull origin main
# or explicitly
git merge origin/main
If there are conflicts, see "Error #5" below.
Option B: Rebase (Linear history, cleaner but rewrites commits)
git pull --rebase origin main
# or explicitly
git rebase origin/main
When to use what?
- Merge: Default, safe, preserves full history
- Rebase: If you want clean linear history and your commits are not yet pushed
Step 5: Push
git push origin main
Force Push (Dangerous!)
If you're absolutely sure you want to overwrite remote:
# DANGER: This deletes other people's commits
git push --force origin main
# Safer alternative (rejects if remote has unexpected commits)
git push --force-with-lease origin main
Never force push to shared branches (main, develop) in team environments.
Error #3: Fatal: Not a Git Repository
Symptom
$ git status
fatal: not a git repository (or any of the parent directories): .git
Root Cause
You're not inside a Git repository. The .git directory is missing or you're in the wrong folder.
Step 1: Check Current Directory
pwd
ls -la
Look for .git directory. If missing, you're not in a repo.
Step 2: Navigate to Repository
cd /path/to/your/repo
git status
Step 3: Or Initialize New Repo
If this is a new project:
git init
git add .
git commit -m "Initial commit"
Step 4: Connect to Remote
git remote add origin git@github.com:user/repo.git
git push -u origin main
Common Mistake: Nested Repositories
You might have accidentally run git init in a parent directory of an existing repo, creating nested Git repositories.
Check:
find . -name ".git" -type d
If you see multiple .git directories, you have nested repos. Remove the unwanted one:
rm -rf /path/to/unwanted/.git
Error #4: Detached HEAD State
Symptom
$ git status
HEAD detached at abc1234
nothing to commit, working tree clean
Or:
$ git checkout abc1234
Note: switching to 'abc1234'.
You are in 'detached HEAD' state.
What is Detached HEAD?
Normally, HEAD points to a branch (like main). In detached HEAD, HEAD points directly to a commit (not a branch).
This happens when you:
git checkout <commit-hash>git checkout <tag>- Rebase operations gone wrong
Problem: Commits made in this state are "orphaned" and will be lost if you switch branches without creating a reference.
Step 1: Check Status
git status
Output shows:
HEAD detached at abc1234
Step 2: Decide What to Do
Scenario A: You haven't made commits, just switch back
git checkout main
Scenario B: You made commits and want to keep them
Create a branch from current position:
git branch temp-branch
git checkout temp-branch
# or combine both
git switch -c temp-branch
Now merge into main:
git checkout main
git merge temp-branch
git branch -d temp-branch # Delete temp branch
Scenario C: You made commits but want to discard them
Just switch back:
git checkout main
# Commits in detached HEAD are abandoned (they'll be garbage collected eventually)
Step 3: Verify
git status
# On branch main
# nothing to commit, working tree clean
Prevention
Avoid checking out commit hashes directly. If you need to inspect old code:
# Instead of:
git checkout abc1234
# Do:
git checkout -b temp-inspect abc1234
# Now you're on a branch, not detached
Error #5: Merge Conflict
Symptom
$ git merge feature-branch
Auto-merging src/app.js
CONFLICT (content): Merge conflict in src/app.js
Automatic merge failed; fix conflicts and then commit the result.
Root Cause
Git can't automatically merge because the same lines were modified in both branches differently.
Step 1: Identify Conflicted Files
git status
Output:
On branch main
You have unmerged paths.
(fix conflicts and run "git commit")
Unmerged paths:
(use "git add <file>..." to mark resolution)
both modified: src/app.js
Step 2: Open Conflicted File
cat src/app.js
You'll see conflict markers:
function greet() {
<<<<<<< HEAD
console.log("Hello from main");
=======
console.log("Hello from feature");
>>>>>>> feature-branch
}
Explanation:
<<<<<<< HEAD- Your current branch version=======- Separator>>>>>>> feature-branch- Incoming changes
Step 3: Resolve Conflict Manually
Edit the file, decide which version to keep (or combine both):
Option A: Keep main version
function greet() {
console.log("Hello from main");
}
Option B: Keep feature version
function greet() {
console.log("Hello from feature");
}
Option C: Combine both
function greet() {
console.log("Hello from main and feature");
}
Remove conflict markers (<<<<<<<, =======, >>>>>>>).
Step 4: Mark as Resolved
git add src/app.js
Step 5: Complete Merge
git commit
# Git will open editor with pre-filled merge commit message
# Save and exit
Or provide message directly:
git commit -m "Merge feature-branch, resolved conflicts in app.js"
Step 6: Verify
git status
# On branch main
# nothing to commit, working tree clean
git log --oneline -5
# Shows merge commit
Using Merge Tools
Instead of manual editing, use a visual merge tool:
# Configure merge tool (one-time setup)
git config --global merge.tool vimdiff
# Resolve conflicts
git mergetool
Other tools: meld, kdiff3, Beyond Compare, VS Code.
Aborting Merge
If you want to cancel the merge:
git merge --abort
This returns you to pre-merge state.
Error #6: Push Rejected (Non-Fast-Forward)
Symptom
$ git push origin main
To github.com:user/repo.git
! [rejected] main -> main (non-fast-forward)
error: failed to push some refs to 'github.com:user/repo.git'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart.
Root Cause
Similar to Error #2, but specifically means you need to incorporate remote changes first.
Solution
# Fetch and merge
git pull origin main
# Resolve conflicts if any (see Error #5)
# Push again
git push origin main
Error #7: Authentication Failed (HTTPS)
Symptom
$ git push origin main
Username for 'https://github.com': yourname
Password for 'https://yourname@github.com':
remote: Support for password authentication was removed on August 13, 2021.
fatal: Authentication failed
Root Cause
GitHub (and GitLab) no longer accept passwords for HTTPS. You need a Personal Access Token (PAT).
Step 1: Create Personal Access Token
GitHub:
- Settings → Developer settings → Personal access tokens → Tokens (classic)
- Generate new token
- Select scopes:
repo(full control of private repositories) - Copy token (save it somewhere, you won't see it again)
Step 2: Use Token as Password
git push origin main
# Username: yourname
# Password: <paste-token-here>
Step 3: Cache Credentials
To avoid re-entering token:
# Cache for 1 hour (3600 seconds)
git config --global credential.helper 'cache --timeout=3600'
# Or store permanently (less secure)
git config --global credential.helper store
Alternative: Switch to SSH
# Check current remote
git remote -v
# origin https://github.com/user/repo.git (fetch)
# Change to SSH
git remote set-url origin git@github.com:user/repo.git
# Verify
git remote -v
# origin git@github.com:user/repo.git (fetch)
Common Mistakes and Best Practices
Mistake #1: Force Pushing to Shared Branches
Never do:
git push --force origin main
On team projects, this deletes other people's work.
Correct approach:
# Always pull first
git pull origin main
# Then push
git push origin main
Mistake #2: Committing Secrets
# Accidentally committed .env file with API keys
git add .env
git commit -m "Add env"
Damage control:
# Remove from last commit (if not yet pushed)
git rm --cached .env
git commit --amend --no-edit
# Add to .gitignore
echo ".env" >> .gitignore
git add .gitignore
git commit -m "Ignore .env"
If already pushed, the secret is compromised. Rotate the key immediately.
Mistake #3: Not Using .gitignore
Always ignore:
- Dependencies (
node_modules/,vendor/) - Build artifacts (
dist/,build/,*.o) - Environment files (
.env) - IDE files (
.vscode/,.idea/)
Create .gitignore:
# .gitignore
node_modules/
.env
dist/
*.log
.DS_Store
Mistake #4: Huge Commits
Bad:
git add .
git commit -m "stuff"
Good:
# Commit logical chunks
git add src/feature-a.js
git commit -m "feat: implement feature A"
git add src/feature-b.js
git commit -m "feat: implement feature B"
Git Workflow Best Practices
1. Feature Branch Workflow
# Create feature branch
git checkout -b feature/user-auth
# Make commits
git add .
git commit -m "feat: add user authentication"
# Before merging, update from main
git checkout main
git pull origin main
git checkout feature/user-auth
git rebase main
# Merge
git checkout main
git merge feature/user-auth
git push origin main
# Delete feature branch
git branch -d feature/user-auth
2. Commit Message Convention
Use conventional commits:
feat: add new feature
fix: fix bug
docs: update documentation
style: code formatting
refactor: code refactoring
test: add tests
chore: maintenance tasks
3. Always Pull Before Push
# Good habit
git pull origin main
git push origin main
4. Use git status Frequently
Before committing:
git status
# Check what you're about to commit
git diff
# See exact changes
5. Protect Main Branch
On GitHub/GitLab, enable branch protection:
- Require pull request reviews
- Require status checks to pass
- Prevent force push
Quick Reference: Git Recovery Commands
# Undo last commit (keep changes)
git reset --soft HEAD~1
# Undo last commit (discard changes)
git reset --hard HEAD~1
# Undo changes to a file
git checkout -- filename
# Revert a pushed commit (safe, creates new commit)
git revert <commit-hash>
# Recover deleted branch
git reflog
git checkout -b recovered-branch <commit-hash>
# Clean untracked files
git clean -fd
# Stash current changes
git stash
git stash pop
# View commit history
git log --oneline --graph --all
Troubleshooting Workflow
When Git operation fails:
- Read the error message carefully
- Run
git statusto understand current state - Check remote status:
git fetch && git status - If authentication issue: check SSH keys or tokens
- If merge issue: read conflict markers, resolve manually
- If push rejected: pull first, then push
- Always test on a backup branch if unsure
Conclusion
Git errors are fixable once you understand what they mean:
- Permission denied: SSH key or token issue
- Push rejected: Need to pull remote changes first
- Detached HEAD: Create a branch from current position
- Merge conflict: Manually edit, remove markers, commit
Master these patterns and Git becomes a reliable tool, not a source of panic.
Pro Tip: Before any destructive operation (
reset --hard,force push), create a backup branch:git branch backup. You can always recover from backup if things go wrong.