Git
Git is a version control system that tracks changes in source code during software development. Created by Linus Torvalds in 2005, Git has become the de facto standard for version control, enabling developers to collaborate effectively, maintain history, and manage code across teams and projects.
What is Git and Why Use It?
Git is a version control system that records changes to files over time, allowing you to recall specific versions later. Think of it as a sophisticated "undo" system with branching capabilities.
Key benefits:
- History tracking: Every change is recorded with who, what, when, and why
- Collaboration: Multiple developers can work on the same codebase simultaneously
- Branching: Create isolated environments for features without affecting main code
- Backup: Distributed nature means every clone is a full backup
- Experimentation: Try new ideas without risk to production code
- Code review: Changes can be reviewed before merging into main codebase
Real-world scenarios:
- "What did the code look like last Tuesday?"
- "Who changed this function and why?"
- "I need to test two different approaches simultaneously"
- "Revert the breaking change from yesterday"
- "Show me what changed between version 1.0 and 2.0"
Git vs GitHub
It's crucial to understand that Git and GitHub are different things.
Git
- Local version control system
- Runs on your machine
- Command-line tool
- Free and open source
- Works completely offline
- Created by Linus Torvalds
GitHub
- Cloud hosting service for Git repositories
- Web-based platform
- Provides remote storage for Git repos
- Adds collaboration features (Pull Requests, Issues, Actions)
- Owned by Microsoft
- Alternative platforms include GitLab, Bitbucket, Gitea
Analogy: Git is like email (the protocol/technology), while GitHub is like Gmail (a service that uses the technology).
You can use Git without GitHub, but you can't use GitHub without Git.
The Two-Stage Commit
Git uses a unique two-stage process for saving changes, giving you precise control over what gets committed.
The Three States
Files in a Git repository exist in one of three states:
Working Directory → Staging Area → Repository
(modified) (staged) (committed)
1. Working Directory: Where you edit files
- Files you've changed but not yet staged
git statusshows these as "modified"
2. Staging Area (Index): A holding area for the next commit
- Files you've marked to be included in the next commit
- Allows you to craft precise commits
git statusshows these as "staged" or "to be committed"
3. Repository: The committed history
- Permanent record in the Git database
- Includes commit message, author, timestamp
- Identified by unique SHA hash
Why Two Stages?
The staging area allows you to:
- Commit related changes together while leaving other changes uncommitted
- Review what will be committed before actually committing
- Stage parts of a file (hunks) rather than the entire file
- Build logical, atomic commits
Example workflow:
# Edit multiple files
code file1.js file2.js file3.js
# Stage only related changes
git add file1.js file2.js
# Commit staged files
git commit -m "Add user authentication"
# file3.js remains uncommitted for later
Common Git Commands
Repository Management
# Initialize new Git repository
git init
# Clone existing repository
git clone https://github.com/user/repo.git
# Check repository status
git status
# View commit history
git log
git log --oneline --graph --all # Prettier view
Basic Workflow
# Add files to staging area
git add filename.txt # Stage specific file
git add . # Stage all changes
git add *.js # Stage all .js files
# Commit staged changes
git commit -m "Commit message"
git commit -am "Message" # Stage and commit tracked files
# View changes
git diff # Working directory vs staging
git diff --staged # Staging vs last commit
# Remove files
git rm filename.txt # Delete and stage removal
# Move/rename files
git mv oldname.txt newname.txt
Branching
# List branches
git branch # Local branches
git branch -a # All branches including remote
# Create branch
git branch feature-name
# Switch branches
git checkout feature-name
git switch feature-name # Newer syntax
# Create and switch in one command
git checkout -b feature-name
git switch -c feature-name
# Merge branch into current branch
git merge feature-name
# Delete branch
git branch -d feature-name # Safe delete
git branch -D feature-name # Force delete
Remote Operations
# Add remote repository
git remote add origin https://github.com/user/repo.git
# View remotes
git remote -v
# Push changes to remote
git push origin main
git push -u origin feature-name # Push and set upstream
# Pull changes from remote
git pull origin main # Fetch and merge
git fetch origin # Fetch without merging
# Clone repository
git clone https://github.com/user/repo.git
Undoing Changes
# Discard changes in working directory
git checkout -- filename.txt
# Unstage files (keep changes in working directory)
git reset HEAD filename.txt
# Undo last commit (keep changes staged)
git reset --soft HEAD~1
# Undo last commit (keep changes in working directory)
git reset HEAD~1
# Undo last commit (discard all changes) ⚠️
git reset --hard HEAD~1
# Create new commit that undoes previous commit
git revert <commit-hash>
Example Commit Process (Main Branch)
Direct commits to the main branch - suitable for solo projects or quick fixes.
# 1. Check current status
git status
# 2. Make changes to files
code README.md
code src/app.js
# 3. Review changes
git diff
# 4. Stage changes
git add README.md src/app.js
# 5. Verify what's staged
git status
# 6. Commit with descriptive message
git commit -m "Update README and fix login bug"
# 7. Push to remote repository
git push origin main
# 8. Verify commit on remote
git log --oneline
Commit Message Best Practices
Well-written commit messages are crucial for maintaining a readable project history. They help team members understand changes and make debugging easier.
General Guidelines:
- Use present tense: "Add feature" not "Added feature"
- A useful tip is to think about prepending the phrase: This commit will... to the message, e.g. This commit will add rate limiting functionality to the API - do not actually add that phrase though!
- Be concise but descriptive
- First line is summary (50 chars or less)
- Separate body with blank line if needed
- Explain "why" not just "what"
Conventional Commits
Conventional Commits is a specification for commit messages that provides a standardized format. It makes commit history easier to read and enables automated tools for versioning and changelog generation.
Format:
<type>(<scope>): <description>
[optional body]
[optional footer]
Common Types:
feat:- New feature for the userfix:- Bug fixdocs:- Documentation changesstyle:- Code style changes (formatting, missing semicolons, etc.)refactor:- Code refactoring without changing functionalitytest:- Adding or updating testschore:- Maintenance tasks, dependency updatesperf:- Performance improvementsci:- CI/CD configuration changesbuild:- Build system or dependency changes
Examples:
# Feature addition
git commit -m "feat(auth): add JWT token refresh mechanism"
# Bug fix
git commit -m "fix(payment): resolve null pointer in payment processor"
# Documentation
git commit -m "docs(readme): update installation instructions"
# Refactoring
git commit -m "refactor(database): improve connection pooling performance"
# Breaking change (note the !)
git commit -m "feat(api)!: change user endpoint response format"
With Body and Footer:
git commit -m "feat(auth): add two-factor authentication
Implements TOTP-based 2FA for enhanced security.
Users can enable 2FA in their account settings.
Closes #123"
Poor commit messages to avoid:
git commit -m "updates"
git commit -m "fixed stuff"
git commit -m "asdf"
git commit -m "WIP"
Benefits of Conventional Commits:
- Automatic changelog generation
- Semantic versioning automation
- Easier to navigate commit history
- Better collaboration and code reviews
- Clearer understanding of impact (feature vs fix vs breaking change)
Feature Branch Workflow (Pull Request)
Creating a feature branch for new work - standard practice in team environments.
1. Start from Updated Main
# Ensure you're on main branch
git checkout main
# Pull latest changes
git pull origin main
2. Create Feature Branch
# Create and switch to new branch
git checkout -b feature/add-user-profile
# Naming conventions:
# feature/description - new features
# bugfix/description - bug fixes
# hotfix/description - urgent production fixes
# refactor/description - code refactoring
3. Make Changes and Commit
# Make your changes
code src/components/UserProfile.js
code src/api/users.js
# Stage and commit regularly
git add src/components/UserProfile.js
git commit -m "Add UserProfile component"
git add src/api/users.js
git commit -m "Add user API endpoints"
# Make multiple small, logical commits
4. Push Feature Branch
# Push branch to remote
git push -u origin feature/add-user-profile
# -u sets upstream tracking for future pushes
# Subsequent pushes can use: git push
5. Create Pull Request
On GitHub (or GitLab, Bitbucket):
- Navigate to repository
- Click "Pull Requests" or "Merge Requests"
- Click "New Pull Request"
- Select base branch (main) and compare branch (feature/add-user-profile)
- Add title and description
- Assign reviewers
- Create Pull Request
6. Address Review Comments
# Make requested changes
code src/components/UserProfile.js
# Commit changes
git add src/components/UserProfile.js
git commit -m "Address review comments: improve error handling"
# Push updates
git push
# PR automatically updates with new commits
7. Merge and Clean Up
After PR approval:
# Merge via GitHub UI or command line
# If using command line:
git checkout main
git pull origin main
git merge feature/add-user-profile
git push origin main
# Delete feature branch
git branch -d feature/add-user-profile # Local
git push origin --delete feature/add-user-profile # Remote
8. Update Local Repository
# Switch to main
git checkout main
# Pull merged changes
git pull origin main
# Clean up local references to deleted remote branches
git fetch --prune
Branch Strategies
Main Branch Only
- Simple, suitable for solo developers
- Direct commits to main
- No Pull Requests
Feature Branch + PR
- Industry standard for teams
- Isolated development
- Code review before merging
- Prevents breaking main branch
Git Flow
- Multiple long-lived branches (main, develop)
- Release and hotfix branches
- More complex, suitable for release-based projects
Trunk-Based Development
- Short-lived feature branches
- Frequent integration to main
- Feature flags for incomplete features
- Suitable for continuous deployment
Best Practices
Do:
- Commit often with meaningful messages
- Keep commits small and focused
- Pull before pushing to avoid conflicts
- Create branches for new features
- Review changes before committing
- Use
.gitignoreto exclude unnecessary files
Don't:
- Commit secrets, passwords, or API keys
- Commit generated files or dependencies
- Force push to shared branches
- Leave commits with "TODO" or "WIP" messages
- Commit large binary files
- Work directly on main in team projects
Common Workflows Summary
# Quick fix on main
git pull origin main
# make changes
git add .
git commit -m "Fix critical bug"
git push origin main
# New feature with PR
git checkout main
git pull origin main
git checkout -b feature/new-feature
# make changes
git add .
git commit -m "Implement new feature"
git push -u origin feature/new-feature
# Create PR on GitHub
Git is powerful but becomes intuitive with practice. The key is understanding the fundamental concepts of commits, branches, and remote repositories. Once these click, Git becomes an invaluable tool in your development workflow.
Further Reading: