A comprehensive guide to Git — from first commit to advanced workflows.
Table of Contents
- What is Git?
- Installing Git
- Initial Configuration
- Core Concepts
- Starting a Repository
- The Three Stages of Git
- Basic Commands
- Branching
- Merging
- Rebasing
- Remote Repositories
- Undoing Things
- Stashing
- Tagging
- Git Log & History
- Git Diff
- .gitignore
- Advanced Topics
- Git Workflows
- Useful Tips & Tricks
- Quick Reference Cheat Sheet
What is Git?
Git is a distributed version control system (DVCS) created by Linus Torvalds in 2005. It tracks changes in source code during software development, enabling multiple developers to work on the same project simultaneously without conflicts.
Why Use Git?
- History tracking — Every change is recorded. You can go back to any version at any time.
- Collaboration — Multiple people can work on the same codebase concurrently.
- Branching — Experiment freely without affecting the main codebase.
- Backup — With remote repositories, your code is safe even if your machine fails.
- Speed — Almost all operations are local and extremely fast.
Git vs. Other Version Control Systems
| Feature | Git | SVN | CVS |
|---|
| Architecture | Distributed | Centralized | Centralized |
| Offline work | Yes | Limited | No |
| Branching | Lightweight | Expensive | Difficult |
| Speed | Fast | Slower | Slow |
Installing Git
macOS
1
2
3
4
| # Using Homebrew (recommended)
brew install git
# Or download from https://git-scm.com
|
Windows
Download the installer from https://git-scm.com/download/win.
Or use winget:
1
| winget install --id Git.Git -e --source winget
|
Linux (Ubuntu/Debian)
1
2
| sudo apt update
sudo apt install git
|
Linux (Fedora/RHEL)
Verify Installation
1
2
| git --version
# git version 2.43.0
|
Initial Configuration
Before using Git, tell it who you are. This information is attached to every commit you make.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # Set your name
git config --global user.name "Your Name"
# Set your email
git config --global user.email "you@example.com"
# Set your default editor (e.g., VS Code)
git config --global core.editor "code --wait"
# Set default branch name to 'main'
git config --global init.defaultBranch main
# View all config settings
git config --list
|
The --global flag applies the setting to all repositories on your machine. Omit it to set it only for the current repository.
Config is stored in ~/.gitconfig (global) or .git/config (local).
Core Concepts
Understanding these four concepts will unlock everything else in Git.
Repository (Repo)
A repository is a directory that Git is tracking. It contains:
- Your project files
- A hidden
.git/ folder where Git stores all history and metadata
Commit
A commit is a snapshot of your project at a specific point in time. Each commit has:
- A unique SHA-1 hash (e.g.,
a3f9c2d) - Author and timestamp
- A commit message
- A reference to its parent commit(s)
Branch
A branch is a lightweight, movable pointer to a commit. The default branch is usually called main (or master in older projects). Branches let you develop features in isolation.
HEAD
HEAD is a special pointer that tells Git where you currently are in the repository. It usually points to the tip (latest commit) of the currently checked-out branch.
Starting a Repository
Initialize a New Repository
1
2
3
4
| # Create a new directory and initialize Git
mkdir my-project
cd my-project
git init
|
This creates a .git/ folder inside my-project/.
Clone an Existing Repository
1
2
3
4
5
6
7
8
9
10
11
| # Clone via HTTPS
git clone https://github.com/user/repo.git
# Clone via SSH
git clone git@github.com:user/repo.git
# Clone into a specific folder name
git clone https://github.com/user/repo.git my-folder
# Clone only the latest commit (shallow clone, faster)
git clone --depth 1 https://github.com/user/repo.git
|
The Three Stages of Git
This is the most important mental model in Git.
1
2
| Working Directory → Staging Area (Index) → Repository (.git/)
(Modify) (git add) (git commit)
|
| Stage | Description |
|---|
| Working Directory | Where you edit files. Changes here are “untracked” or “modified”. |
| Staging Area | A holding area where you prepare changes before committing. |
| Repository | The permanent history of committed snapshots. |
Think of it like this: the staging area is your draft before you publish (commit).
Basic Commands
Checking Status
This is your best friend. It tells you:
- Which files are untracked
- Which files are modified
- Which files are staged
Adding Files to Staging
1
2
3
4
5
6
7
8
9
10
11
| # Stage a specific file
git add filename.txt
# Stage multiple files
git add file1.txt file2.txt
# Stage all changes in the current directory
git add .
# Stage changes interactively (choose hunks)
git add -p
|
Committing
1
2
3
4
5
6
7
8
| # Commit with a message
git commit -m "Add login feature"
# Stage all tracked files AND commit (skips staging step)
git commit -am "Fix typo in README"
# Commit and open editor for a detailed message
git commit
|
Writing Good Commit Messages:
A good commit message follows this pattern:
1
2
3
4
5
6
7
| Short summary (50 chars max)
Optional body explaining the WHY, not the what.
Wrap lines at 72 characters.
- Use bullet points if needed
- Reference issues: Closes #42
|
Removing Files
1
2
3
4
5
| # Remove file from Git and filesystem
git rm filename.txt
# Remove from Git tracking only (keep the file locally)
git rm --cached filename.txt
|
Moving / Renaming Files
1
| git mv old-name.txt new-name.txt
|
Branching
Branches are one of Git’s most powerful features. They are cheap, fast, and encourage experimentation.
Creating Branches
1
2
3
4
5
6
7
8
| # Create a new branch
git branch feature/login
# Create and switch to a new branch (modern syntax)
git switch -c feature/login
# Create and switch (older syntax)
git checkout -b feature/login
|
Switching Branches
1
2
3
4
5
| # Modern syntax
git switch main
# Older syntax
git checkout main
|
Listing Branches
1
2
3
4
5
6
7
8
| # List local branches
git branch
# List remote branches
git branch -r
# List all branches (local + remote)
git branch -a
|
Renaming a Branch
1
2
3
4
5
| # Rename current branch
git branch -m new-name
# Rename a specific branch
git branch -m old-name new-name
|
Deleting a Branch
1
2
3
4
5
6
7
8
| # Delete a merged branch (safe)
git branch -d feature/login
# Force delete (even if not merged)
git branch -D feature/login
# Delete a remote branch
git push origin --delete feature/login
|
Merging
Merging integrates changes from one branch into another.
Fast-Forward Merge
When the target branch has no new commits since the feature branch diverged, Git simply moves the pointer forward. No new merge commit is created.
1
2
3
| git switch main
git merge feature/login
# Fast-forward: main pointer moves to feature/login tip
|
Three-Way Merge
When both branches have diverged, Git creates a merge commit that ties both histories together.
1
2
3
| git switch main
git merge feature/login
# Creates a merge commit automatically
|
Merge with No Fast-Forward
Force a merge commit even if fast-forward is possible. Useful to preserve feature branch history.
1
| git merge --no-ff feature/login
|
Merge Conflicts
A conflict happens when two branches modify the same part of a file differently.
1
2
| git merge feature/login
# CONFLICT (content): Merge conflict in app.js
|
Git marks the conflict in the file:
1
2
3
4
5
| <<<<<<< HEAD
const greeting = "Hello";
=======
const greeting = "Hi there";
>>>>>>> feature/login
|
To resolve:
- Edit the file to keep the correct version
- Remove the conflict markers (
<<<<<<<, =======, >>>>>>>) - Stage the resolved file:
git add app.js - Complete the merge:
git commit
To abort a merge: git merge --abort
Rebasing
Rebasing is an alternative to merging that rewrites history to create a cleaner, linear timeline.
1
2
3
| # Rebase feature branch onto main
git switch feature/login
git rebase main
|
Instead of creating a merge commit, rebasing replays your feature commits on top of the latest main.
Interactive Rebase
Interactive rebase lets you rewrite, squash, reorder, or edit commits before sharing them.
1
2
| # Rebase the last 3 commits interactively
git rebase -i HEAD~3
|
Commands available in the editor:
| Command | Description |
|---|
pick | Keep the commit as-is |
reword | Keep the commit, but edit the message |
edit | Pause and amend the commit |
squash | Combine commit into the previous one |
fixup | Like squash, but discard the commit message |
drop | Remove the commit entirely |
⚠️ Golden Rule of Rebasing: Never rebase commits that have been pushed to a shared remote branch. Rewriting shared history causes problems for other contributors.
Remote Repositories
Remote repositories are versions of your project hosted on the internet or a network (e.g., GitHub, GitLab, Bitbucket).
Managing Remotes
1
2
3
4
5
6
7
8
9
10
11
| # View remotes
git remote -v
# Add a remote
git remote add origin https://github.com/user/repo.git
# Rename a remote
git remote rename origin upstream
# Remove a remote
git remote remove origin
|
Fetching, Pulling, and Pushing
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Fetch: Download changes but don't merge them into your working branch
git fetch origin
# Pull: Fetch + Merge (updates your current branch)
git pull origin main
# Pull with rebase instead of merge
git pull --rebase origin main
# Push: Upload your commits to remote
git push origin main
# Push and set upstream tracking (first time)
git push -u origin main
# Push all branches
git push --all origin
|
Tracking Remote Branches
After setting upstream with -u, you can just run git pull or git push without specifying the remote and branch.
1
2
3
4
| git push -u origin feature/login
# Later:
git push # knows to push to origin/feature/login
git pull # knows to pull from origin/feature/login
|
Undoing Things
Amend the Last Commit
Fix the last commit message or add forgotten files.
1
2
3
4
5
6
| # Change just the commit message
git commit --amend -m "New, correct message"
# Add a forgotten file to the last commit
git add forgotten-file.txt
git commit --amend --no-edit
|
⚠️ Avoid amending pushed commits. It rewrites history.
Unstage a File
1
2
3
4
5
| # Remove from staging area, keep changes in working directory
git restore --staged filename.txt
# Older syntax
git reset HEAD filename.txt
|
Discard Changes in Working Directory
1
2
3
4
5
| # Discard unstaged changes in a file (PERMANENT)
git restore filename.txt
# Discard all unstaged changes (PERMANENT)
git restore .
|
Reverting a Commit (Safe)
git revert creates a new commit that undoes a previous one. This is safe for shared branches because it doesn’t rewrite history.
Resetting (Careful!)
git reset moves the branch pointer and optionally affects the staging area and working directory.
1
2
3
4
5
6
7
8
| # Soft reset: Move HEAD, keep changes staged
git reset --soft HEAD~1
# Mixed reset (default): Move HEAD, unstage changes
git reset HEAD~1
# Hard reset: Move HEAD, DISCARD all changes (PERMANENT)
git reset --hard HEAD~1
|
When to use which:
--soft: “I want to recommit these changes differently”--mixed: “I want to unstage and review before recommitting”--hard: “I want to completely throw away these commits”
Recovering Lost Commits with Reflog
Even after a hard reset, commits aren’t immediately gone. git reflog records every HEAD movement.
1
2
3
4
5
6
| git reflog
# a3f9c2d HEAD@{0}: reset: moving to HEAD~1
# b7e1a3f HEAD@{1}: commit: Add feature X
# Restore the lost commit
git reset --hard b7e1a3f
|
Stashing
Stashing temporarily saves your uncommitted changes so you can switch context without committing unfinished work.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| # Stash current changes
git stash
# Stash with a descriptive name
git stash push -m "WIP: half-done login form"
# Include untracked files in stash
git stash -u
# List all stashes
git stash list
# stash@{0}: WIP: half-done login form
# stash@{1}: On main: quick fix attempt
# Apply the most recent stash (keeps it in the stash list)
git stash apply
# Apply a specific stash
git stash apply stash@{1}
# Apply and remove from stash list
git stash pop
# Delete a specific stash
git stash drop stash@{0}
# Delete all stashes
git stash clear
|
Tagging
Tags mark specific points in history, typically used for release versions.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| # Create a lightweight tag
git tag v1.0.0
# Create an annotated tag (recommended for releases)
git tag -a v1.0.0 -m "Release version 1.0.0"
# Tag a specific commit
git tag -a v0.9.0 a3f9c2d -m "Beta release"
# List all tags
git tag
# List tags matching a pattern
git tag -l "v1.*"
# Show tag details
git show v1.0.0
# Push a tag 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
|
Git Log & History
Viewing Commit History
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
| # Full log
git log
# Compact one-line log
git log --oneline
# With branch graph
git log --oneline --graph --all
# Show last N commits
git log -5
# Commits by a specific author
git log --author="Jane Doe"
# Commits containing a keyword in the message
git log --grep="fix"
# Commits since a date
git log --since="2024-01-01"
# Commits that changed a specific file
git log -- path/to/file.txt
# Show what changed in each commit
git log -p
# Summarize changes (files changed, insertions, deletions)
git log --stat
|
1
2
| git log --pretty=format:"%h - %an, %ar : %s"
# a3f9c2d - Jane Doe, 2 days ago : Add login feature
|
Common format placeholders:
| Placeholder | Meaning |
|---|
%h | Abbreviated commit hash |
%H | Full commit hash |
%an | Author name |
%ae | Author email |
%ar | Author date, relative |
%s | Subject (commit message) |
Git Diff
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
| # Diff between working directory and staging area (unstaged changes)
git diff
# Diff between staging area and last commit (staged changes)
git diff --staged
# Diff between two commits
git diff a3f9c2d b7e1a3f
# Diff between two branches
git diff main feature/login
# Diff a specific file
git diff HEAD -- filename.txt
# Summarize diff (just show which files changed)
git diff --stat
|
.gitignore
The .gitignore file tells Git which files and directories to ignore.
Creating a .gitignore
Create a file named .gitignore in your repository root:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
| # Ignore node_modules
node_modules/
# Ignore build output
dist/
build/
# Ignore environment files
.env
.env.local
# Ignore OS files
.DS_Store
Thumbs.db
# Ignore log files
*.log
# Ignore all .txt files in a specific folder
docs/*.txt
# Ignore everything in a directory except one file
secrets/*
!secrets/README.md
|
Global .gitignore
Set up a global ignore file for OS/editor files across all repos:
1
| git config --global core.excludesfile ~/.gitignore_global
|
Ignoring Already-Tracked Files
If a file is already tracked, adding it to .gitignore won’t stop Git from tracking it. You need to untrack it first:
1
2
| git rm --cached filename.txt
git commit -m "Stop tracking filename.txt"
|
Advanced Topics
Cherry-Picking
Apply a specific commit from one branch to another.
1
| git cherry-pick a3f9c2d
|
Useful when you want to bring a single bug fix from a feature branch into main without merging the whole branch.
Git Bisect
Binary search through your history to find which commit introduced a bug.
1
2
3
4
5
6
7
8
9
10
11
| git bisect start
git bisect bad # Current commit is broken
git bisect good v1.0.0 # This version was fine
# Git checks out a commit in the middle
# Test your code, then tell Git:
git bisect good # or: git bisect bad
# Git narrows down until it finds the culprit
# When done:
git bisect reset
|
Git Blame
Find out who wrote each line of a file and in which commit.
1
2
3
4
| git blame filename.txt
# Blame a specific range of lines
git blame -L 10,25 filename.txt
|
Submodules
Include one Git repository inside another.
1
2
3
4
5
6
7
8
| # Add a submodule
git submodule add https://github.com/user/library.git libs/library
# Clone a repo with submodules
git clone --recurse-submodules https://github.com/user/repo.git
# Update all submodules
git submodule update --init --recursive
|
Sparse Checkout
Check out only specific directories from a large repository.
1
2
3
4
| git clone --no-checkout https://github.com/user/big-repo.git
cd big-repo
git sparse-checkout set src/module-a docs
git checkout main
|
Git Workflows
GitHub Flow (Simple)
Best for continuous deployment:
main is always deployable- Create a branch for every new feature or fix
- Open a Pull Request when ready
- Review, discuss, and merge into
main - Deploy immediately
1
2
3
| main ────●────────────────────●──── (deploy)
\ /
● feature/login ●
|
Git Flow (Feature-Based)
Best for versioned software with scheduled releases:
main — production-ready codedevelop — integration branchfeature/* — new features (branch from develop)release/* — release preparation (branch from develop)hotfix/* — urgent production fixes (branch from main)
1
2
3
4
5
6
| main ────●───────────────────────●────
\ /
hotfix ● fix-critical-bug ●
develop ──●──────────────────●────────●──
\ /
feature ● feature/x ●
|
Trunk-Based Development
Developers commit directly to main (or short-lived branches merged within a day). Used at large scale with feature flags.
Useful Tips & Tricks
Aliases
Create shortcuts for frequently used commands:
1
2
3
4
5
6
7
8
| git config --global alias.st status
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.lg "log --oneline --graph --all"
# Now you can use:
git st
git lg
|
Search Through Code History
1
2
3
4
5
| # Find which commit added or removed a specific string
git log -S "function login" --source --all
# Search commit messages with regex
git log --all --grep="bug fix" --oneline
|
Save Credentials
1
2
3
4
5
| # Cache credentials for 1 hour (3600 seconds)
git config --global credential.helper "cache --timeout=3600"
# Store credentials permanently (less secure)
git config --global credential.helper store
|
Clean Up Untracked Files
1
2
3
4
5
6
7
8
9
10
11
| # Preview what will be deleted
git clean -n
# Delete untracked files
git clean -f
# Delete untracked files AND directories
git clean -fd
# Delete ignored files too
git clean -fdx
|
Show Who Changed What in a Commit
1
2
| git show a3f9c2d
git show a3f9c2d --stat
|
Find the Merge Base of Two Branches
1
2
| git merge-base main feature/login
# Returns the common ancestor commit hash
|
Quick Reference Cheat Sheet
Setup
1
2
3
4
| git config --global user.name "Name"
git config --global user.email "email"
git init
git clone <url>
|
Stage & Commit
1
2
3
4
5
| git status
git add <file>
git add .
git commit -m "message"
git commit --amend
|
Branching
1
2
3
4
5
6
| git branch # List branches
git switch -c <branch> # Create & switch
git switch <branch> # Switch
git branch -d <branch> # Delete
git merge <branch> # Merge
git rebase <branch> # Rebase
|
Remote
1
2
3
4
5
6
| git remote -v
git remote add origin <url>
git fetch
git pull
git push origin <branch>
git push -u origin <branch>
|
Undo
1
2
3
4
5
| git restore <file> # Discard changes
git restore --staged <file> # Unstage
git revert <commit> # Safe undo
git reset --soft HEAD~1 # Undo commit, keep staged
git reset --hard HEAD~1 # Undo commit, discard changes
|
Inspect
1
2
3
4
5
6
7
| git log --oneline --graph
git diff
git diff --staged
git blame <file>
git show <commit>
git stash list
git reflog
|
Conclusion
Git is an essential tool for any developer, and mastering it will make you significantly more productive and confident. Start with the basics — add, commit, push, pull, and branch — and gradually explore more advanced features like rebasing, cherry-picking, and bisecting as you need them.
The best way to learn Git is to use it daily. Don’t be afraid to experiment — as long as you haven’t force-pushed to a shared branch, almost anything can be undone.