Working with Git for the first time? We’ve all been there! Let’s go through the top mistakes and how to avoid them.
Early Mistakes: Branches, Commits, Files and Security
1. Committing Directly to main/master
❌ Mistake:
Doing everything straight in main:
# Bad
git add .
git commit -m "fix"
git push origin main
✅ Right approach:
Create branches for each task:
# Good
git checkout -b feature/add-login
# ... make your changes ...
git add .
git commit -m "feat: add user login form"
git push origin feature/add-login
# Then open a Pull Request
Why it matters:
- main should stay stable
- Easier to revert changes
- You can work on multiple tasks in parallel
- Code review before merging
2. Bad Commit Messages
❌ Bad examples:
git commit -m "fix"
git commit -m "update"
git commit -m "asdfasdf"
git commit -m "commit"
git commit -m "done"
✅ Good examples:
git commit -m "fix: fix email validation error"
git commit -m "feat: add export to PDF button"
git commit -m "docs: update API documentation"
git commit -m "refactor: optimize database query"
The good message formula:
<type>: <what was done>
feat - new feature
fix - bug fix
docs - documentation
refactor - refactoring
test - tests
chore - routine tasks
3. Committing Everything Blindly
❌ Mistake:
git add .
git commit -m "changes"
The commit ends up containing:
- .env with passwords 😱
- node_modules/ (millions of files)
- .DS_Store (macOS junk)
- __pycache__/ (compiled Python)
- Personal notes
✅ Right approach:
Set up .gitignore first:
# .gitignore
.env
*.pyc
__pycache__/
node_modules/
.DS_Store
*.log
.vscode/
.idea/
Check before committing:
git status # what will be added?
git diff # what changed?
git add file1.py file2.py # add selectively
git commit -m "feat: add auth module"
4. Pushing Secrets to the Repository
❌ DANGEROUS:
# settings.py
SECRET_KEY = "django-insecure-key-123456789"
DATABASE_PASSWORD = "mypassword123"
API_KEY = "sk_live_51234567890abcdef"
☠️ All of this becomes public if the repository is Public!
✅ Right approach:
Use environment variables:
# settings.py
import os
SECRET_KEY = os.getenv('SECRET_KEY')
DATABASE_PASSWORD = os.getenv('DB_PASSWORD')
API_KEY = os.getenv('API_KEY')
Create a .env file (DO NOT commit it!):
# .env
SECRET_KEY=django-insecure-key-123456789
DB_PASSWORD=mypassword123
API_KEY=sk_live_51234567890abcdef
Add to .gitignore:
echo ".env" >> .gitignore
git add .gitignore
git commit -m "chore: add .gitignore for .env"
Create a .env.example for the team:
# .env.example
SECRET_KEY=your-secret-key-here
DB_PASSWORD=your-database-password
API_KEY=your-api-key
Sync and Code Quality Mistakes
5. Not Pulling Before Pushing
❌ Mistake:
# You're working locally...
git commit -m "fix: bug"
git push origin main
# ❌ Error: Updates were rejected
A colleague already pushed changes!
✅ Right approach:
# Pull first
git pull origin main
# Resolve conflicts if any
# Then push
git push origin main
Even better — use rebase:
git pull --rebase origin main
git push origin main
6. Being Afraid of Conflicts
What this looks like:
git pull origin main
# <<< Auto-merging file.py
# <<< CONFLICT (content): Merge conflict in file.py
Don’t panic! Conflicts are normal.
How to resolve:
# file.py
<<<<<<< HEAD
def hello():
print("Hello") # Your version
=======
def hello():
print("Hi") # Version from origin
>>>>>>> origin/main
Steps:
- Open the file
- Remove the markers
<<<<<<<,=======,>>>>>>> - Choose the code you want (or merge both)
- Save the file
# Final version
def hello():
print("Hello") # Kept our version
- Commit:
git add file.py
git commit -m "fix: resolve conflict in file.py"
git push
7. Not Using Branches
❌ Bad:
Everyone works in main → constant conflicts.
✅ Git Flow (simplified):
# Main branches
main (or master) - stable code
develop - development
# Feature branches
feature/login - new feature
feature/payment - another feature
# Bugfix branches
bugfix/fix-email - bug fix
Workflow:
# New feature
git checkout main
git pull origin main
git checkout -b feature/user-profile
# ... work ...
git add .
git commit -m "feat: add user profile"
git push origin feature/user-profile
# Create a Pull Request on GitHub
# After approval:
git checkout main
git pull origin main # get updated main with your feature
git branch -d feature/user-profile # delete local branch
8. Huge Commits
❌ Bad:
# 3 days of work, 500 files changed
git add .
git commit -m "done"
Impossible to:
- Understand what changed
- Revert a specific part
- Do a code review
✅ Good:
Make small commits:
# Day 1
git add auth/login.py
git commit -m "feat: add login form"
git add auth/validators.py
git commit -m "feat: email and password validators"
# Day 2
git add auth/views.py
git commit -m "feat: auth view"
git add auth/urls.py
git commit -m "feat: auth routes"
Rule: 1 commit = 1 logical change.
9. Committing Broken Code
❌ Bad:
# Code doesn't work, tests fail
git add .
git commit -m "wip"
git push
Now everyone has broken code!
✅ Right approach:
Before committing:
# Run tests
pytest
# Check code
python manage.py check
# Make sure it runs
python manage.py runserver
Only then:
git add .
git commit -m "feat: add feature X"
git push
If you need to save unfinished work:
git stash # stash changes
git stash list # view stash list
git stash pop # restore changes
Ignoring Important Git Conventions
10. Ignoring .gitignore
❌ Mistake:
After git add . these end up committed:
- 📦
node_modules/(200 MB!) - 🗄️ Database
db.sqlite3 - 🖼️ Uploaded files
media/ - 📝 Logs
debug.log
✅ .gitignore template for Python/Django:
# .gitignore
# Python
*.pyc
__pycache__/
*.py[cod]
*.so
*.egg
*.egg-info/
dist/
build/
# Django
*.log
db.sqlite3
media/
staticfiles/
# Environment
.env
.venv/
venv/
ENV/
# IDE
.vscode/
.idea/
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Testing
.coverage
htmlcov/
.pytest_cache/
11. Not Using git status
❌ Blind commands:
git add .
git commit -m "update"
git push
✅ Check before every action:
git status # what changed?
# You'll see:
# Changes not staged:
# modified: file1.py
# deleted: old_file.py
# Untracked files:
# new_file.py
git diff file1.py # what exactly changed?
git add file1.py new_file.py # add consciously
git status # check again
git commit -m "feat: update file1, add new_file"
12. Force Push to a Shared Branch
❌ DANGEROUS:
git push --force origin main
☠️ This rewrites history for everyone! May destroy a colleague’s work.
✅ Right approach:
Force push only to your own branches:
# Your personal feature branch
git push --force origin feature/my-work
For main/master use:
git push origin main # without --force!
If you need force push, use the safe version:
git push --force-with-lease origin main
# Checks that history hasn't changed since your last pull
13. Deleting Branches Without Checking
❌ Mistake:
git branch -D feature/important
# Oops, there was unpushed code there! 😱
✅ Right approach:
Check before deleting:
# Are changes pushed?
git branch -vv # shows remote tracking info
# Is the branch merged?
git branch --merged # list of merged branches
# Safe delete (only if merged)
git branch -d feature/task # -d refuses to delete unmerged branches
# Forced (use carefully!)
git branch -D feature/task # -D deletes regardless
General Tips
Use a GUI if you’re nervous
- GitHub Desktop - simple interface
- GitKraken - powerful and beautiful
- Sourcetree - free from Atlassian
- VS Code - built-in Git
Learn to undo mistakes
# Undo last commit (changes stay)
git reset --soft HEAD~1
# Undo changes in a file
git checkout -- file.py
# Return to a specific commit
git revert <commit-hash>
# View history
git log --oneline --graph --all
Back up before experimenting
# Create a backup branch
git checkout -b backup/before-experiment
# Experiment in another branch
git checkout -b experiment
# If something broke, go back
git checkout backup/before-experiment
Pre-push Checklist
- [ ]
git status- checked what I’m adding - [ ]
git diff- reviewed the changes - [ ] Tests pass
- [ ]
.envand secrets are not in the commit - [ ] Commit message is meaningful
- [ ]
git pullbeforegit push - [ ] Working in the correct branch
Follow these rules and you’ll avoid 90% of Git problems! 🎯
💬 Comments (0)
No comments yet
Be the first to share your opinion about this article!