Whether you're working solo on a side project or collaborating with a team of 50 developers, Git is the backbone of modern software development. It tracks every change you make, lets you experiment safely with branches, and makes collaboration possible without stepping on each other's code.
This guide walks through Git from the ground up — what it is, why you need it, and a detailed, hands-on breakdown of the commands you'll actually use every day, along with the ones you'll reach for once things get a little more advanced.
What is Git and Why Do You Need It?
Git is a distributed version control system (VCS) that tracks changes to files over time. "Distributed" means every developer has a full copy of the project's history on their own machine — not just the latest files, but every commit ever made. This is what makes Git fast, reliable, and usable even without an internet connection.
Git lets you:
- Save "snapshots" of your project at any point (commits)
- Go back to a previous version if something breaks
- Work on new features without affecting the main codebase (branching)
- Merge work from multiple people without overwriting each other's changes
- Keep a full history of who changed what, and when, and why
Git vs a Traditional Save System
| Aspect | Without Git | With Git |
|---|---|---|
| Change tracking | Manual (copy-pasting folders like project_final_v2) |
Automatic, precise history of every change |
| Collaboration | Risk of overwriting others' work | Merge and conflict resolution built in |
| Rollback | Difficult or impossible | Instant — revert to any previous commit |
| Branching | Not possible without duplicating folders | Native, lightweight branching |
| Accountability | No record of who changed what | Full commit history with author and timestamp |
| Offline work | Depends on manual backups | Full history available locally, no network needed |
Git vs GitHub — A Common Point of Confusion
It's worth clarifying this early: Git is the version control tool itself (installed on your machine). GitHub (along with GitLab, Bitbucket, etc.) is a cloud platform that hosts Git repositories and adds collaboration features like pull requests, issues, and code review. You can use Git without ever touching GitHub — but GitHub makes sharing and collaborating far easier.
Installing and Setting Up Git
Before using Git, install it for your OS (via git-scm.com, a package manager like apt/brew/choco), then configure your identity — this information gets attached to every commit you make.
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
Some other useful one-time configuration steps:
git config --global init.defaultBranch main # set default branch name to "main"
git config --global core.editor "code --wait" # set VS Code as your commit message editor
git config --global color.ui auto # enable colored output in the terminal
Check your current configuration anytime:
git config --list # view all settings
git config user.name # view a specific setting
Git config exists at three levels — --local (this repo only), --global (this user, all repos), and --system (all users on the machine). Local settings override global, which override system.
Starting a Project: init vs clone
There are two ways to start working with Git on a project.
git init — Start a New Repository
Turns your current folder into a Git repository, creating a hidden .git folder that stores all version history.
git init
git init my-project # create the folder and initialize it in one step
git clone — Copy an Existing Repository
Downloads a full copy of an existing repository (including all its history) from a remote source like GitHub.
git clone https://github.com/username/repository.git
git clone https://github.com/username/repository.git custom-folder-name
git clone --depth 1 https://github.com/username/repository.git # shallow clone, history only from the latest commit
init vs clone at a glance:
git init |
git clone |
|---|---|
| Creates a brand-new, empty repository | Copies an existing repository |
| Used when starting a project from scratch | Used when joining an existing project |
| No remote connection by default | Automatically sets up the remote (origin) |
The Core Git Workflow
Understanding Git means understanding its three (or four) areas:
- Working Directory — Your actual project files, where you make edits
- Staging Area (Index) — A holding area for changes you're about to commit
- Local Repository (.git) — Where committed snapshots are permanently stored on your machine
-
Remote Repository — The shared copy hosted on a server (e.g., GitHub), synced via
push/pull
Here's the typical day-to-day flow, step by step:
Step 1: Check the Status
See what's changed, what's staged, and what's untracked.
git status # detailed status
git status -s # short, condensed format
Step 2: Ignore Files You Don't Want Tracked
Before staging, it's worth setting up a .gitignore file so build artifacts, dependencies, and secrets never get committed by accident.
# .gitignore example
node_modules/
.env
*.log
dist/
.DS_Store
Git will simply skip any file or folder pattern listed here when you run git add.
Step 3: Stage Your Changes
Move changes from the working directory into the staging area, preparing them for a commit.
git add filename.txt # stage a specific file
git add folder/ # stage everything in a folder
git add . # stage all changed files in the current directory and below
git add -A # stage all changes across the entire repo, including deletions
git add -p # interactively choose which changes (hunks) to stage
git add . vs git add -A:
git add . |
git add -A |
|---|---|
| Stages new and modified files in current directory and below | Stages all changes across the entire repository |
| May miss deleted files outside current directory | Includes deletions everywhere |
Step 4: Commit Your Changes
Save a permanent snapshot of the staged changes, with a message describing what changed.
git commit -m "Add login functionality"
git commit -am "Fix navbar alignment bug" # stage tracked files + commit in one step
git commit --amend -m "Corrected commit message" # edit the most recent commit
git commit --amend --no-edit # add staged changes to the last commit without changing its message
Tip: Only amend commits that haven't been pushed yet — amending shared history causes conflicts for collaborators.
Good commit messages matter. A widely used convention:
<type>: <short summary>
<optional longer description>
Example: fix: correct off-by-one error in pagination logic
Common types: feat, fix, docs, style, refactor, test, chore.
Step 5: View History
git log # full commit history
git log --oneline # condensed, one line per commit
git log --oneline --graph # visual branch history
git log --author="Mohit" # filter commits by author
git log -p filename.txt # show changes made to a specific file over time
git show <commit-hash> # show details and diff of one specific commit
git blame filename.txt # see who last modified each line of a file
Step 6: Push to a Remote Repository
Upload your local commits to a remote server like GitHub.
git push origin main
git push -u origin main # push and set upstream tracking (only needed once per branch)
git push # after upstream is set, just this works
git push --force-with-lease # safer force-push that fails if remote has new commits you don't have
Caution: Avoid
git push --forceon shared branches — it overwrites remote history and can wipe out teammates' work.--force-with-leaseis the safer alternative since it checks first.
Step 7: Fetch and Pull Updates
git fetch origin # download remote changes WITHOUT merging them into your branch
git pull origin main # fetch AND merge remote changes into your current branch
git pull --rebase # fetch and rebase instead of merge, for a cleaner history
fetch vs pull:
git fetch |
git pull |
|---|---|
| Downloads changes only | Downloads AND merges changes |
| Safe — doesn't touch your working files | Can create merge commits or conflicts immediately |
| Lets you review before merging | Applies changes right away |
Branching: Working on Features Independently
Branches let you work on new features or fixes without touching the main codebase until you're ready.
git branch # list all local branches
git branch -a # list all branches, including remote-tracking ones
git branch feature/login # create a new branch
git checkout feature/login # switch to that branch
git checkout -b feature/login # create AND switch in one command
git switch feature/login # modern alternative to checkout
git switch -c feature/login # modern alternative to checkout -b
git branch -m old-name new-name # rename a branch
Merging Branches
Once your feature is ready, merge it back into the main branch.
git checkout main
git merge feature/login
git merge --no-ff feature/login # always create a merge commit, even for fast-forward merges (preserves feature history)
Resolving Merge Conflicts
Conflicts happen when the same lines of a file were changed differently on two branches. Git will pause the merge and mark the conflicting sections in the file:
<<<<<<< HEAD
your current branch's version
=======
the incoming branch's version
>>>>>>> feature/login
To resolve it:
- Open the file and manually edit it to keep the correct content (removing the
<<<<<<<,=======,>>>>>>>markers) - Stage the resolved file —
git add filename.txt - Complete the merge —
git commit(Git pre-fills a merge commit message for you)
If you want to back out of a conflicted merge entirely:
git merge --abort
Deleting a Branch
git branch -d feature/login # safe delete (only if merged)
git branch -D feature/login # force delete (even if not merged)
git push origin --delete feature/login # delete the branch on the remote too
checkout/switch vs merge:
| Command | Purpose |
|---|---|
git checkout / git switch
|
Moves you between branches |
git merge |
Combines changes from one branch into another |
Undoing Changes: Choosing the Right Command
Git offers multiple ways to undo work, depending on how far you want to go back.
git restore — Undo Uncommitted Changes
git restore filename.txt # discard changes in working directory
git restore --staged filename.txt # unstage a file (keep the edits)
git restore --source=HEAD~1 filename.txt # restore a file to how it was one commit ago
git reset — Move the Commit Pointer Backward
git reset --soft HEAD~1 # undo last commit, keep changes staged
git reset --mixed HEAD~1 # undo last commit, keep changes unstaged (this is the default mode)
git reset --hard HEAD~1 # undo last commit AND discard all changes permanently
git reset <commit-hash> # reset to a specific commit
git revert — Safely Undo a Commit (Recommended for Shared Branches)
Instead of erasing history, revert creates a new commit that undoes a previous one — safer for branches others are working on.
git revert <commit-hash>
git revert HEAD # revert the most recent commit
git revert --no-commit <commit-hash> # revert changes without auto-committing, so you can review first
reset vs revert:
git reset |
git revert |
|---|---|
| Rewrites history (dangerous on shared branches) | Preserves history, adds a new "undo" commit |
| Good for local, unpushed commits | Good for commits already pushed/shared |
Can permanently lose changes (--hard) |
Never loses commit history |
git reflog — Your Safety Net
Even after a reset --hard, Git usually hasn't actually deleted your commits yet. reflog shows a log of everywhere HEAD has pointed, letting you recover "lost" commits.
git reflog
git checkout <commit-hash-from-reflog>
Working with Remotes
git remote -v # view connected remotes and their URLs
git remote add origin <repository-url> # connect a remote repository
git remote remove origin # disconnect a remote
git remote rename origin upstream # rename a remote
git remote show origin # detailed info about a remote, including tracked branches
Stashing: Temporarily Saving Work
If you need to switch branches but aren't ready to commit, stash saves your uncommitted changes without a commit.
git stash # save current changes
git stash save "WIP: navbar fix" # save with a descriptive message
git stash list # view all stashes
git stash pop # reapply the most recent stash and remove it from the list
git stash apply # reapply a stash but keep it in the list
git stash apply stash@{2} # apply a specific stash by index
git stash drop stash@{0} # delete a specific stash
git stash clear # delete all stashes
Comparing Changes
git diff # changes in working directory not yet staged
git diff --staged # changes staged but not yet committed
git diff branch1 branch2 # differences between two branches
git diff HEAD~2 HEAD # differences between two commits
Rewriting and Combining Commits
git rebase — Reapply Commits on Top of Another Branch
Rewrites commit history to create a cleaner, linear project history (compared to merge, which preserves branch structure).
git checkout feature/login
git rebase main
git rebase -i HEAD~3 # interactive rebase — reorder, edit, squash, or drop the last 3 commits
During an interactive rebase, Git opens an editor listing your commits with options like pick, squash, reword, and drop next to each one — letting you clean up messy commit history before merging.
If conflicts occur during a rebase:
# fix the conflicted files, then:
git add .
git rebase --continue
# or to cancel entirely:
git rebase --abort
merge vs rebase:
git merge |
git rebase |
|---|---|
| Preserves full branch history | Creates a clean, linear history |
| Adds a merge commit | No extra merge commit |
| Safe for shared/public branches | Best used on local, unpushed branches |
git cherry-pick — Apply a Specific Commit from Another Branch
Useful when you need just one specific fix from another branch, without merging everything.
git cherry-pick <commit-hash>
git cherry-pick <hash1> <hash2> # cherry-pick multiple commits
Tagging Releases
Tags mark specific points in history — commonly used for release versions (v1.0, v2.1.3).
git tag v1.0 # create a lightweight tag
git tag -a v1.0 -m "Version 1.0" # create an annotated tag with a message
git tag # list all tags
git push origin v1.0 # push a specific tag
git push origin --tags # push all tags
git tag -d v1.0 # delete a local tag
git push origin --delete v1.0 # delete a tag from the remote
Bonus: Submodules and Aliases
Git Submodules
Submodules let you include one Git repository inside another — useful for shared libraries.
git submodule add https://github.com/user/library.git libs/library
git submodule update --init --recursive
Git Aliases — Save Yourself Some Typing
git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.st status
git config --global alias.cm "commit -m"
Now git st works exactly like git status.
Common Git Commands Cheat Sheet
| Command | Purpose |
|---|---|
git init |
Initialize a new repository |
git clone <url> |
Copy an existing repository |
git status |
Show current state of working directory |
git add . |
Stage all changes |
git commit -m "message" |
Save a snapshot with a message |
git push |
Upload commits to remote |
git fetch |
Download remote changes without merging |
git pull |
Download and merge remote changes |
git branch |
List/create branches |
git checkout / git switch
|
Switch branches |
git merge |
Combine branch changes |
git log |
View commit history |
git diff |
View unstaged/staged differences |
git stash |
Temporarily save uncommitted changes |
git reset |
Move commit pointer, optionally discarding changes |
git revert |
Undo a commit safely with a new commit |
git rebase |
Reapply commits for a linear history |
git cherry-pick |
Apply a specific commit to the current branch |
git tag |
Mark a specific commit (e.g., a release) |
git reflog |
Recover lost commits or view HEAD history |
A Realistic Step-by-Step Example
Here's what a typical feature development flow looks like end to end:
-
Update your local main branch —
git checkout main && git pull origin main -
Create a new branch —
git checkout -b feature/user-profile - Make your code changes in your editor
-
Check what changed —
git status -
Stage the changes —
git add . -
Commit with a clear message —
git commit -m "feat: add user profile page" -
Push the branch to remote —
git push -u origin feature/user-profile - Open a Pull Request on GitHub/GitLab for review
-
Address review feedback — make edits, then
git add .andgit commit --amend --no-editor a new commit, then push again - Resolve any merge conflicts if the main branch has moved forward
- Merge into main once approved, then delete the feature branch — both locally and on the remote
Wrapping Up
Git can feel overwhelming at first because of how many commands it has, but in practice, you'll use a small core set daily: status, add, commit, push, pull, branch, and merge. The rest — stash, rebase, cherry-pick, revert, reflog — become genuinely useful as your projects grow and your collaboration needs get more complex.
The best way to get comfortable with Git isn't memorizing every command — it's using it daily on real projects until the workflow becomes second nature. Break something, fix it, and you'll understand Git faster than any tutorial can teach you.
Made with care by Mohit Bhadra. If this guide helped you understand Git a little better, feel free to share it with someone who's still afraid of the command line.



Top comments (0)