Git & GitHub

Git Best Practices

13 min Lesson 32 of 35

Git Best Practices

In this lesson, we'll explore essential Git best practices that will help you maintain clean, secure, and efficient repositories. Following these practices will make collaboration easier and prevent common mistakes.

Commit Often, Push Regularly

Frequent commits and regular pushes protect your work and improve collaboration:

Commit Often: ✓ Commit after completing a logical unit of work ✓ Don't wait until end of day to commit ✓ Creates more granular history ✓ Easier to identify when bugs were introduced ✓ Simpler to revert specific changes Push Regularly: ✓ Push at least once per day ✓ Backup your work to remote ✓ Share progress with team ✓ Enable early conflict detection ✓ Prevent large merge conflicts
Golden Rule: Commit locally when a feature or fix works. Push to remote when it's ready for others to see. Think: "Commit for you, push for them."

Keep Commits Atomic and Focused

Atomic commits contain one logical change and make history easier to understand:

Good - Atomic Commits: ✓ "Add user authentication endpoint" ✓ "Fix login button alignment" ✓ "Update API documentation" Bad - Non-Atomic Commits: ✗ "Fix bugs and add features" ✗ "Friday afternoon changes" ✗ "Various updates"

How to stage partial changes:

# Stage specific files git add file1.js file2.css # Interactive staging (patch mode) git add -p # Stage only modified files (not new files) git add -u # Stage everything in a directory git add src/components/
Tip: Use git add -p to review each change before staging. This helps you split unrelated changes into separate commits.

Write Meaningful Commit Messages

Good commit messages explain why changes were made, not just what changed:

Commit Message Structure: <type>(<scope>): <subject> <body> <footer> Example: feat(auth): implement JWT token refresh Add automatic token refresh mechanism to prevent session expiration during active use. Tokens are refreshed 5 minutes before expiration. Closes #123

Conventional Commits Types:

feat: New feature fix: Bug fix docs: Documentation changes style: Code formatting (no logic change) refactor: Code restructuring (no behavior change) test: Adding or updating tests chore: Maintenance tasks (dependencies, build) perf: Performance improvements ci: CI/CD configuration changes revert: Reverting a previous commit
Good Commit Messages: ✓ "fix(api): handle null values in user response" ✓ "refactor(auth): extract validation logic to service" ✓ "docs(readme): add installation instructions" Bad Commit Messages: ✗ "fix stuff" ✗ "update" ✗ "asdfasdf" ✗ "fix fix fix"
Best Practice: Write commit messages in imperative mood: "Add feature" not "Added feature" or "Adds feature". Think: "If applied, this commit will <your message>".

Use Branches Effectively

Proper branch management keeps your codebase organized and collaboration smooth:

Branch Naming Conventions: feature/add-user-profile bugfix/fix-login-error hotfix/critical-security-patch release/v1.2.0 experiment/new-architecture Pattern: <type>/<description> - Use lowercase - Use hyphens, not underscores - Keep it descriptive but concise - Include ticket number if applicable: feature/AUTH-123-login

Branch lifecycle best practices:

# 1. Create branch from updated main git checkout main git pull git checkout -b feature/new-dashboard # 2. Work on feature, commit regularly git add . git commit -m "feat: add dashboard layout" # 3. Keep branch updated with main git checkout main git pull git checkout feature/new-dashboard git merge main # or git rebase main # 4. Push and create PR git push -u origin feature/new-dashboard # 5. After merge, clean up git checkout main git pull git branch -d feature/new-dashboard git remote prune origin
Important: Delete branches after merging. Stale branches clutter the repository and confuse team members about what's active.

Keep History Clean

A clean Git history is easier to navigate and understand:

Before pushing to shared branches: # Squash multiple small commits git rebase -i HEAD~5 # Fix typos in recent commit message git commit --amend # Reorder commits logically git rebase -i main # Clean up "WIP" commits git rebase -i --autosquash
Critical Rule: NEVER rebase or amend commits that have been pushed to shared branches. Only rewrite history on your local feature branches.

Using fixup commits for cleaner history:

# Make initial commit git commit -m "feat: add login form" # Later, fix something in that commit git add . git commit --fixup HEAD # Before pushing, squash fixup commits git rebase -i --autosquash main

Security Considerations

Protect your repositories and sensitive information:

Security Checklist: ✓ Never commit passwords, API keys, or secrets ✓ Use environment variables for sensitive data ✓ Review changes before committing ✓ Enable 2FA on GitHub/GitLab ✓ Use SSH keys or personal access tokens ✓ Regularly rotate credentials ✓ Audit repository access permissions ✓ Enable branch protection rules ✓ Require code review before merging ✓ Scan for secrets in commits

Never Commit Secrets

Accidentally committing secrets is a serious security risk:

Common secrets to avoid: - Database passwords - API keys and tokens - Private keys (.pem, .key files) - OAuth client secrets - AWS/cloud credentials - Encryption keys - Session secrets

If you accidentally commit a secret:

# 1. IMMEDIATELY rotate the exposed credential # 2. Remove it from Git history (if not yet pushed) git reset --soft HEAD~1 # Edit files to remove secret git add . git commit -m "fix: remove accidentally committed secret" # 3. If already pushed, use tools like BFG Repo-Cleaner bfg --replace-text secrets.txt git reflog expire --expire=now --all git gc --prune=now --aggressive # 4. Force push (coordinate with team!) git push --force-with-lease
Critical: Once a secret is pushed to a public repository, consider it compromised FOREVER. Even if you remove it, it exists in history and can be accessed. Always rotate the credential immediately.

.gitignore Best Practices

Proper .gitignore prevents committing unnecessary or sensitive files:

# Environment and secrets .env .env.local *.key *.pem secrets.yml # Dependencies node_modules/ vendor/ *.egg-info/ # Build outputs dist/ build/ *.pyc *.class # IDE files .vscode/ .idea/ *.swp *.swo # OS files .DS_Store Thumbs.db # Logs *.log logs/ # Temporary files tmp/ temp/ *.tmp

Global gitignore for personal preferences:

# Configure global gitignore git config --global core.excludesfile ~/.gitignore_global # Create ~/.gitignore_global with IDE/OS-specific files # Example content: .DS_Store .vscode/ .idea/ *.swp
Pro Tip: Use gitignore.io to generate comprehensive .gitignore files for your tech stack.

Large File Strategies

Git is designed for source code, not large binary files. Handle them properly:

Options for Large Files: 1. Don't commit them (best for temporary files) - Add to .gitignore - Store in cloud storage (S3, Google Drive) 2. Git LFS (for versioned large files) - Images, videos, datasets - Design files (PSD, AI, Sketch) - Large documentation PDFs 3. Separate repository - Keep large assets in different repo - Reference via submodules or package manager

Setting up Git LFS:

# Install Git LFS git lfs install # Track large file types git lfs track "*.psd" git lfs track "*.ai" git lfs track "*.mp4" git lfs track "*.zip" # Commit the .gitattributes file git add .gitattributes git commit -m "chore: configure Git LFS" # Add and commit large files normally git add large-file.psd git commit -m "feat: add design mockup" git push
Size Limits: GitHub has a 100MB file size limit. Files larger than 50MB will generate warnings. Use Git LFS for files over 10MB.

Performance Best Practices

Keep your Git operations fast and efficient:

# Enable parallel processing git config --global checkout.workers 8 # Use shallow clones for CI/CD git clone --depth 1 <url> # Optimize repository git gc --aggressive # Reduce clone size git clone --single-branch --branch main <url> # Enable file system monitor (for large repos) git config core.fsmonitor true # Enable commit graph for faster operations git config core.commitGraph true git commit-graph write --reachable

Code Review Best Practices

Make your commits easy to review:

Before requesting review: ✓ Self-review your changes ✓ Run tests and linters ✓ Update documentation ✓ Squash WIP commits ✓ Write descriptive PR description ✓ Link to related issues ✓ Add screenshots for UI changes ✓ Request specific reviewers ✓ Mark as draft if not ready

Daily Git Workflow Checklist

Follow this daily routine for smooth Git operations:

Morning: 1. git checkout main 2. git pull origin main 3. git checkout <feature-branch> 4. git merge main (or rebase) Throughout day: 5. git add <files> (stage meaningful changes) 6. git commit -m "meaningful message" 7. Repeat as you complete work units End of day: 8. git push origin <feature-branch> 9. git status (ensure clean working directory) Before PR: 10. git rebase -i main (clean up commits) 11. git push --force-with-lease 12. Create pull request

Practice Exercise:

Audit Your Git Practices:

  1. Review your last 10 commits - do they follow best practices?
  2. Check your .gitignore - are all necessary files excluded?
  3. Scan your repository for large files (use du -sh *)
  4. Create a comprehensive .gitignore for your current project
  5. Write 3 commit messages following Conventional Commits
  6. Clean up any merged feature branches
  7. Set up Git LFS if you have large files

Summary

In this lesson, you learned:

  • Commit often and push regularly to protect work
  • Keep commits atomic, focused, and well-described
  • Write meaningful commit messages using Conventional Commits
  • Use branches effectively with clear naming conventions
  • Maintain clean history without rewriting shared commits
  • Security practices: never commit secrets, use .gitignore
  • Handle large files with Git LFS or alternative strategies
  • Optimize Git performance for better efficiency
  • Follow daily workflow checklist for consistency
Next Up: In the next lesson, we'll explore advanced GitHub features that extend beyond basic Git functionality!