Git Version Control Basics
Git is the most widely used version control system in the world. Created by Linus Torvalds in 2005 (the same person who created Linux), Git tracks changes in your files over time, lets you collaborate with other developers, and gives you the ability to revert to any previous version of your project. Whether you are a solo developer or part of a large team, Git is an essential skill.
This tutorial will take you from zero to confident with Git, covering everything from your first commit to pushing code to GitHub.
What is Version Control?
Imagine you are writing an essay. Without version control, you might save files like essay_v1.docx, essay_v2.docx, essay_final.docx, essay_final_REAL.docx. Version control solves this chaos by tracking every change automatically, recording who made it and when, and letting you go back to any previous state.
Git is a distributed version control system, meaning every developer has a complete copy of the project history on their own machine. This makes it fast, reliable, and able to work offline.
Installing Git
# macOS (using Homebrew)
$ brew install git
# Ubuntu/Debian Linux
$ sudo apt update
$ sudo apt install git
# Windows
# Download from https://git-scm.com/download/win
# Or use: winget install Git.Git
# Verify installation
$ git --version
git version 2.43.0
# Configure your identity (required before first commit)
$ git config --global user.name "Your Name"
$ git config --global user.email "you@example.com"
# Optional but recommended settings
$ git config --global init.defaultBranch main
$ git config --global core.editor "code --wait" # Use VS Code
--global flag sets the configuration for all repositories on your machine. You can also configure settings per-repository by omitting this flag while inside a repo.
Creating Your First Repository
A Git repository (or "repo") is a project folder that Git is tracking. Let's create one from scratch:
# Create a new project directory
$ mkdir my-website
$ cd my-website
# Initialize a Git repository
$ git init
Initialized empty Git repository in /home/user/my-website/.git/
# The .git folder contains all version control data
# NEVER delete or manually edit files in .git
That's it. Your folder is now a Git repository. The .git directory that Git creates contains all the tracking information, history, and configuration for this repo.
Staging and Committing: git add and git commit
Git uses a two-step process for saving changes. First you stage changes (choose what to include), then you commit them (save a snapshot). Think of it like packing a box (staging) and then sealing and labeling it (committing).
# Create some files
$ echo "<h1>Hello World</h1>" > index.html
$ echo "body { margin: 0; }" > style.css
# Check the status of your repository
$ git status
On branch main
Untracked files:
(use "git add ..." to include in what will be committed)
index.html
style.css
# Stage a specific file
$ git add index.html
# Stage multiple files
$ git add index.html style.css
# Stage ALL changes in the current directory
$ git add .
# Check status again - staged files appear in green
$ git status
On branch main
Changes to be committed:
(use "git restore --staged ..." to unstage)
new file: index.html
new file: style.css
# Commit with a descriptive message
$ git commit -m "Add initial HTML and CSS files"
[main (root-commit) a1b2c3d] Add initial HTML and CSS files
2 files changed, 2 insertions(+)
Checking History: git status, git log, git diff
# git status - see current state of your working directory
$ git status
On branch main
nothing to commit, working tree clean
# Make some changes
$ echo "p { color: navy; }" >> style.css
$ git status
On branch main
Changes not staged for commit:
modified: style.css
# git diff - see exactly what changed (before staging)
$ git diff
diff --git a/style.css b/style.css
--- a/style.css
+++ b/style.css
@@ -1 +1,2 @@
body { margin: 0; }
+p { color: navy; }
# git diff --staged - see what's staged for commit
$ git add style.css
$ git diff --staged
# git log - view commit history
$ git log
commit a1b2c3d4e5f6 (HEAD -> main)
Author: Your Name
Date: Mon Jan 15 10:30:00 2024
Add initial HTML and CSS files
# Compact log format (one line per commit)
$ git log --oneline
a1b2c3d Add initial HTML and CSS files
# Visual branch graph
$ git log --oneline --graph --all
Branching
Branches are one of Git's most powerful features. A branch is an independent line of development. You can create a branch to work on a new feature, fix a bug, or experiment — all without affecting the main codebase. When you are done, you merge your branch back into main.
# List all branches (* = current branch)
$ git branch
* main
# Create a new branch
$ git branch feature/navbar
# Switch to the new branch
$ git switch feature/navbar
# (or the older command: git checkout feature/navbar)
# Create and switch in one command
$ git switch -c feature/contact-form
# (or: git checkout -b feature/contact-form)
# You are now on a separate branch
# Any commits you make will NOT affect 'main'
$ echo "<nav>Home | About</nav>" > nav.html
$ git add nav.html
$ git commit -m "Add navigation bar HTML structure"
# See all branches and which one you're on
$ git branch
main
* feature/navbar
A common branching convention is to use prefixes: feature/ for new features, bugfix/ for bug fixes, and hotfix/ for urgent production fixes.
Merging Branches
When your feature is complete, you merge it back into the main branch:
# First, switch back to the branch you want to merge INTO
$ git switch main
# Merge the feature branch into main
$ git merge feature/navbar
Updating a1b2c3d..d4e5f6a
Fast-forward
nav.html | 1 +
1 file changed, 1 insertion(+)
# The feature branch's commits are now part of main
# Delete the feature branch (it's been merged)
$ git branch -d feature/navbar
Deleted branch feature/navbar (was d4e5f6a).
Resolving Merge Conflicts
Conflicts happen when two branches modify the same line in a file. Git cannot automatically decide which change to keep, so it asks you to resolve it manually. Do not panic — conflicts are normal and easy to fix once you understand the format.
# When a conflict occurs, Git marks the file:
$ git merge feature/redesign
Auto-merging style.css
CONFLICT (content): Merge conflict in style.css
Automatic merge failed; fix conflicts and then commit the result.
# Open the conflicted file - you'll see markers:
<<<<<<< HEAD
body { background: white; color: #333; }
=======
body { background: #f5f5f5; color: #222; }
>>>>>>> feature/redesign
# Everything between <<<<<<< and ======= is YOUR version (main)
# Everything between ======= and >>>>>>> is THEIR version (feature branch)
# Edit the file to resolve: keep one, the other, or combine both
body { background: #f5f5f5; color: #333; }
# Remove ALL conflict markers (<<<, ===, >>>)
# Then stage and commit:
$ git add style.css
$ git commit -m "Merge feature/redesign, resolve style conflicts"
git merge --abort. Take a breath, review both branches, and try again.
Working with Remote Repositories
So far, everything has been local. Remote repositories (like GitHub, GitLab, or Bitbucket) let you back up your code online and collaborate with others.
# Add a remote repository (usually called "origin")
$ git remote add origin https://github.com/username/my-website.git
# List remotes
$ git remote -v
origin https://github.com/username/my-website.git (fetch)
origin https://github.com/username/my-website.git (push)
# Push your local commits to the remote
$ git push -u origin main
# -u sets "origin main" as the default, so next time just: git push
# Pull changes from the remote (if others have pushed)
$ git pull origin main
# Or simply:
$ git pull
# Clone an existing remote repository
$ git clone https://github.com/username/my-website.git
# This creates a folder, downloads all files and history
The .gitignore File
Some files should never be tracked by Git: passwords, API keys, compiled files, OS junk files, and dependency folders. The .gitignore file tells Git which files to ignore.
# Create a .gitignore file in your project root
# Dependencies
node_modules/
vendor/
# Environment files (contain secrets!)
.env
.env.local
*.env
# Build output
dist/
build/
# OS files
.DS_Store # macOS
Thumbs.db # Windows
*.swp # Vim swap files
# IDE files
.vscode/
.idea/
*.sublime-workspace
# Logs
*.log
logs/
# Compiled files
*.min.js
*.min.css
git rm --cached filename.
Basic GitHub Workflow
Here is the standard workflow used by most teams and open-source projects:
- Clone the repository (or pull the latest changes if you already have it)
- Create a branch for your feature or fix
- Make changes and commit them with clear messages
- Push your branch to GitHub
- Open a Pull Request (PR) on GitHub
- Code review — teammates review your changes
- Merge the PR into main once approved
- Delete the feature branch
- Pull the updated main branch to your local machine
# The full workflow in commands:
# 1. Start fresh
$ git switch main
$ git pull
# 2. Create a feature branch
$ git switch -c feature/add-footer
# 3. Work and commit
$ # ... edit files ...
$ git add .
$ git commit -m "Add footer with social links and copyright"
# 4. Push to GitHub
$ git push -u origin feature/add-footer
# 5. Go to GitHub.com and open a Pull Request
# (GitHub will show a prompt when you visit the repo)
# 7. After PR is merged on GitHub:
$ git switch main
$ git pull
# 8. Clean up local branch
$ git branch -d feature/add-footer
Quick Reference: Essential Git Commands
Key Takeaways
- Git tracks every change to your project, letting you revert, compare, and collaborate.
- The basic workflow is: edit files,
git addto stage,git committo save. - Use
git statusconstantly — it shows you exactly what state your repo is in. - Branch for every new feature or fix. Never work directly on main.
- Merge conflicts are normal. Read the markers carefully, choose the right code, and commit.
- Use .gitignore to exclude sensitive files, dependencies, and OS junk.
- Write descriptive commit messages that explain why changes were made.
- Follow the GitHub workflow: branch, commit, push, pull request, review, merge.