Git & Version Control
Master Git version control system. Learn essential commands, branching strategies, and collaboration workflows used in modern development.
What is Git?
Git is a distributed version control system that tracks changes in source code during software development. Created by Linus Torvalds in 2005 for Linux kernel development, Git is designed for coordinating work among programmers, but it can be used to track changes in any set of files. Git is free, open-source, and has become the standard for version control in the software industry.
Unlike centralized version control systems (like SVN or CVS), Git is distributed, meaning every developer has a full copy of the repository history on their local machine. This makes operations fast and allows developers to work offline. Git's branching model is lightweight and efficient, enabling workflows that were previously impractical.
Why Use Version Control?
Version control is essential for any software project, regardless of size. It provides numerous benefits that improve code quality, collaboration, and project management.
Key Benefits
- Complete History - Track every change made to your codebase, including who made it and when
- Collaboration - Multiple developers can work on the same project simultaneously
- Branching and Merging - Work on features in isolation without affecting the main code
- Disaster Recovery - Easily recover from mistakes by reverting to previous versions
- Blame and Tracking - See who made what changes for accountability and debugging
- Code Review - Review changes before they are merged into the main codebase
- Release Management - Tag and manage releases with confidence
Installing Git
Installing on macOS
# Using Homebrew
brew install git
# Or download from git-scm.com
Installing on Linux
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install git
# CentOS/Fedora
sudo dnf install git
Installing on Windows
Download Git for Windows from git-scm.com. The installer includes Git Bash, a terminal that provides a Unix-like command-line experience on Windows. Git is also available through Windows Package Manager (winget) and Chocolatey.
Configuring Git
Before using Git, you should configure your identity. This information is used in every commit you make.
# Set your name
git config --global user.name "Your Name"
# Set your email
git config --global user.email "your.email@example.com"
# Set default branch name
git config --global init.defaultBranch main
# Set default editor
git config --global core.editor "code --wait"
# View your configuration
git config --list
# Enable helpful colorization
git config --global color.ui auto
Git Basics
Creating a Repository
# Initialize a new repository in current directory
git init
# Initialize a repository in a specific directory
git init my-project
# Clone an existing repository
git clone https://github.com/user/repo.git
# Clone into a specific directory
git clone https://github.com/user/repo.git my-folder
# Clone a specific branch
git clone -b develop https://github.com/user/repo.git
The Three States
Git has three main states that your files can reside in:
- Modified - You have changed the file but have not staged it yet
- Staged - You have marked a modified file to go into your next commit
- Committed - The data is safely stored in your local database
These three states correspond to three main sections of a Git project:
- Working Directory - Your local filesystem where you edit files
- Staging Area (Index) - A file that stores information about what will go into your next commit
- Git Directory (Repository) - Where Git stores the metadata and object database for your project
Basic Workflow Commands
# Check status of your working directory
git status
# Add a specific file to staging
git add filename.txt
# Add all changes to staging
git add .
# Add all changes, including deletions
git add -A
# Commit staged changes with a message
git commit -m "Add new feature"
# Add and commit in one command (for tracked files)
git commit -am "Fix bug in login"
# View commit history
git log
# View compact history
git log --oneline
# View history with graph
git log --oneline --graph --all
Working with Branches
Branches are one of Git's most powerful features. They allow you to develop features, fix bugs, or experiment in isolation from the main codebase. Creating branches in Git is extremely fast and lightweight.
Branch Commands
# List all local branches
git branch
# List all branches including remote
git branch -a
# Create a new branch
git branch feature/login
# Switch to a branch
git checkout feature/login
# Create and switch to a new branch
git checkout -b feature/signup
# Modern way to switch branches (Git 2.23+)
git switch feature/login
# Create and switch with git switch
git switch -c feature/payment
# Delete a branch (after merging)
git branch -d feature/login
# Force delete a branch
git branch -D feature/abandoned
# Rename current branch
git branch -m new-name
# Rename a specific branch
git branch -m old-name new-name
Merging Branches
# Switch to the target branch (e.g., main)
git checkout main
# Merge another branch into current branch
git merge feature/login
# Merge with a commit message
git merge feature/login -m "Merge feature/login into main"
# Abort a merge in case of conflicts
git merge --abort
Resolving Merge Conflicts
Merge conflicts occur when Git cannot automatically reconcile differences between two commits. You must manually resolve these conflicts before completing the merge.
# When a conflict occurs, Git marks the file:
<<<<<<< HEAD
Your changes on the current branch
=======
Changes from the branch being merged
>>>>>>> feature/login
# After resolving conflicts, stage the file
git add resolved-file.txt
# Complete the merge
git commit
Rebasing
Rebasing is an alternative to merging that creates a linear commit history. It replays commits from one branch onto another, making it appear as if you developed your feature from the latest main branch.
Rebase Commands
# Rebase current branch onto main
git rebase main
# Interactive rebase to edit commits
git rebase -i HEAD~3
# Abort a rebase in progress
git rebase --abort
# Continue after resolving conflicts
git rebase --continue
# Skip the current patch
git rebase --skip
When to Use Merge vs Rebase
- Use Merge - When you want to preserve the complete history and branch structure
- Use Rebase - When you want a clean, linear history
- Never Rebase - Commits that have been pushed to a shared repository
Remote Repositories
Remote repositories are versions of your project hosted on the Internet or network. You can have multiple remote repositories for pushing, pulling, and collaborating.
Remote Commands
# List remote repositories
git remote -v
# Add a remote repository
git remote add origin https://github.com/user/repo.git
# Add additional remote
git remote add upstream https://github.com/original/repo.git
# Remove a remote
git remote remove origin
# Rename a remote
git remote rename origin upstream
# Show remote details
git remote show origin
Push and Pull
# Push to remote
git push origin main
# Push and set upstream
git push -u origin main
# Push all branches
git push --all origin
# Pull changes from remote
git pull origin main
# Pull with rebase instead of merge
git pull --rebase origin main
# Fetch changes without merging
git fetch origin
# Fetch all remotes
git fetch --all
Undoing Changes
Git provides several ways to undo changes, depending on where those changes are in the workflow.
Unstaging and Discarding Changes
# Unstage a file (keep changes in working directory)
git restore --staged filename.txt
# Discard changes in working directory
git restore filename.txt
# Discard all changes in working directory
git restore .
# Legacy command to unstage
git reset HEAD filename.txt
# Legacy command to discard changes
git checkout -- filename.txt
Amending Commits
# Amend the last commit message
git commit --amend -m "New commit message"
# Add more changes to the last commit
git add forgotten-file.txt
git commit --amend --no-edit
Reverting and Resetting
# Create a new commit that undoes a previous commit
git revert abc1234
# Revert the last commit
git revert HEAD
# Reset to a previous commit (keeps changes staged)
git reset --soft HEAD~1
# Reset to a previous commit (keeps changes in working directory)
git reset --mixed HEAD~1
# Reset to a previous commit (discards all changes)
git reset --hard HEAD~1
Stashing Changes
Stashing temporarily shelves changes so you can work on something else. It is useful when you need to switch branches but are not ready to commit your current work.
# Stash current changes
git stash
# Stash with a message
git stash save "Work in progress on feature X"
# List all stashes
git stash list
# Apply the most recent stash
git stash pop
# Apply a specific stash
git stash apply stash@{2}
# View stash contents
git stash show -p stash@{0}
# Drop a stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
Git Log and History
Git provides powerful tools for exploring the commit history of a project.
# View commit history
git log
# Compact view
git log --oneline
# Show graph of branches
git log --oneline --graph --all
# Show last n commits
git log -5
# Show commits by author
git log --author="John"
# Show commits in date range
git log --since="2024-01-01" --until="2024-12-31"
# Show commits that changed a specific file
git log -- filename.txt
# Search commit messages
git log --grep="fix"
# Show the changes in each commit
git log -p
# Show statistics
git log --stat
Comparing Changes
# Show unstaged changes
git diff
# Show staged changes
git diff --staged
# Compare two branches
git diff main feature/login
# Compare specific commits
git diff abc1234 def5678
# Compare with a specific file
git diff HEAD filename.txt
# Show word-level diff
git diff --word-diff
Tagging
Tags are used to mark specific points in repository history, typically for releases.
# List all tags
git tag
# Create a lightweight tag
git tag v1.0.0
# Create an annotated tag
git tag -a v1.0.0 -m "Version 1.0.0 release"
# Tag a specific commit
git tag -a v1.0.0 abc1234
# Push tags to remote
git push origin v1.0.0
# Push all tags
git push origin --tags
# Delete a local tag
git tag -d v1.0.0
# Delete a remote tag
git push origin --delete v1.0.0
Branching Strategies
Git Flow
Git Flow is a branching model that uses multiple branches for different purposes:
- main - Production-ready code
- develop - Integration branch for features
- feature/* - New features developed in isolation
- release/* - Preparation for new release
- hotfix/* - Quick fixes for production issues
GitHub Flow
GitHub Flow is a simpler alternative suitable for continuous deployment:
- Create a branch from main
- Add commits
- Open a Pull Request
- Discuss and review
- Deploy and test
- Merge to main
Trunk-Based Development
In trunk-based development, developers collaborate on code in a single branch (trunk/main), avoiding long-lived feature branches. This works well with feature flags and continuous integration.
The .gitignore File
The .gitignore file tells Git which files or directories to ignore. This prevents sensitive data, build artifacts, and temporary files from being committed.
# Common .gitignore entries:
# Dependencies
node_modules/
vendor/
__pycache__/
# Build output
dist/
build/
*.pyc
# IDE settings
.vscode/
.idea/
# Environment files
.env
.env.local
# OS files
.DS_Store
Thumbs.db
# Logs
*.log
logs/
# Temporary files
*.tmp
*.swp
Git Hooks
Git hooks are scripts that run automatically at certain points in the Git workflow. They enable automation of tasks like code linting, testing, and commit message validation.
Common Hooks
- pre-commit - Run before a commit is created
- commit-msg - Validate or modify commit messages
- pre-push - Run before pushing to remote
- post-merge - Run after a merge completes
# Example pre-commit hook (.git/hooks/pre-commit)
#!/bin/sh
# Run linting
npm run lint
# Run tests
npm test
# If tests fail, abort the commit
if [ $? -ne 0 ]; then
echo "Tests failed. Commit aborted."
exit 1
fi
Collaboration Workflows
Pull Requests (Merge Requests)
Pull Requests are a way to propose changes to a repository. They provide a platform for code review, discussion, and approval before merging.
Forking Workflow
- Fork the repository to your account
- Clone your fork locally
- Add the original repository as upstream
- Create a feature branch
- Make changes and push to your fork
- Create a Pull Request to the original repository
# Clone your fork
git clone https://github.com/your-username/repo.git
# Add upstream remote
git remote add upstream https://github.com/original-owner/repo.git
# Keep fork updated
git fetch upstream
git checkout main
git merge upstream/main
Git Best Practices
Commit Messages
- Use the imperative mood ("Add feature" not "Added feature")
- Keep the first line under 50 characters
- Separate subject from body with a blank line
- Explain why, not what (the code shows what)
- Reference issue numbers when applicable
General Practices
- Commit often with small, focused changes
- Pull frequently to stay up to date
- Review your changes before committing (git diff)
- Use branches for all new work
- Never commit sensitive data
- Write meaningful commit messages
- Test before pushing
Conclusion
Git is an essential tool for modern software development. Mastering its commands and workflows enables effective collaboration, maintains a reliable history of changes, and provides the foundation for continuous integration and deployment practices. Start with the basics and gradually explore advanced features as your needs grow.
# Initialize a new repository
git init
# Clone a repository
git clone https://github.com/user/repo.git
# Check status
git status
# Add files to staging
git add .
git add filename.txt
# Commit changes
git commit -m "Add new feature"
# Push to remote
git push origin main
# Pull latest changes
git pull origin main
# View commit history
git log --oneline -10
# Create and switch to a new branch
git checkout -b feature/user-auth
# Or with newer Git syntax
git switch -c feature/user-auth
# List all branches
git branch -a
# Switch to existing branch
git checkout main
git switch main
# Merge feature branch into main
git checkout main
git merge feature/user-auth
# Delete branch after merge
git branch -d feature/user-auth
# Rebase instead of merge
git checkout feature/user-auth
git rebase main