Git Basics¶
Learning Objectives
Initialize and clone Git repositories for version control
Stage, commit, and push changes with meaningful messages
Configure
.gitignoreto exclude build artifacts and secretsUnderstand the three-stage workflow: working directory → staging → repository
Pull from and push to remotes while avoiding conflicts
Recover from mistakes using
git resetwith appropriate modes
Git is a distributed version control system that tracks changes to files over time. Unlike centralized systems, every developer has a complete copy of the repository history, enabling offline work and redundancy.
Why use Git?
Track every change with timestamps and author information
Revert to any previous state instantly
Collaborate without overwriting each other’s work
Experiment safely in branches without affecting the main codebase
This guide covers essential operations with the reasoning behind each step.
Initial Setup¶
Configure your identity once per system. Git embeds this information in every commit, creating an immutable audit trail of who made each change.
git config --global user.name "Your Name"
git config --global user.email "your.email@example.com"
Creating a Repository¶
Local Repository¶
Initialize a repository when you want to start version-controlling a project. The -b main flag creates the default branch named “main” instead of the legacy “master”.
# Navigate to your project directory first
cd my-project
# Create the hidden .git directory that stores all version history
# This directory contains commits, branches, tags, and configuration
# It's hidden (starts with .) to keep it out of your way
git init -b main
The git init command creates a .git/ subdirectory containing:
Object database (all commits, trees, blobs)
References (branch heads, tags)
Configuration and hooks
Working tree metadata
Clone Existing Repository¶
Cloning downloads a complete repository history from a remote server, not just the current files.
git clone <repository-url>
cd <repository-name>
Adding Files¶
Git refers to versioning files as “tracking” them. Git uses a three-stage workflow: working directory → staging area (index) → repository. The staging area lets you craft commits by selecting exactly which changes to include.
# Stage specific files for the next commit
# Only changes to these files will be included
git add file1.c file2.h
# Add all files in current directory
git add .
# Add all tracked files with changes
git add -u
Why stage incrementally? Creating focused commits (one logical change per commit) makes history easier to navigate and revert if needed.
Ignoring Files¶
Create a .gitignore file to prevent unnecessary files from being tracked. Git will never track ignored files unless explicitly forced.
# Example .gitignore
.env
Why ignore .env? Contains secrets (API keys, passwords) that should never be versioned or shared.
*.o
*.pyc
__pycache__/
build/
*.log
Patterns apply recursively to all subdirectories. A file matching *.log in any subdirectory is ignored.
Common ignore patterns:
Build artifacts (
*.o,build/) - regenerateable, bloat repoDependencies (
node_modules/,venv/) - install from manifestSecrets (
.env,*.pem) - security risk if committedEditor files (
.vscode/,*.swp) - local configuration
Checking Status¶
The git status command shows the state of the working directory and staging area.
Shows:
Changes not staged for commit: Modified files Git will NOT include in next commit (you must
git addthem)Changes to be committed: Staged files Git WILL include in next commit
Untracked files: New files Git doesn’t know about yet (not in
.gitignore)
git status
Committing Changes¶
A commit is a snapshot of your staged changes, with metadata (author, timestamp, message).
# Interactive commit (opens editor)
git commit
# Commit with message
git commit -m "Description of changes"
# Commit all modified tracked files (skips unstaged files)
git commit -am "Description of changes"
# View diff while committing
git commit -v -m "Description"
Tip
To configure your default commit message editor:
git config --global core.editor vim
# Or for VS Code:
git config --global core.editor "code --wait"
**Why commit messages matter:** Good messages explain *why* a change was made, not just *what*. Future you (or collaborators) will thank you when debugging months later.
**Commit message structure:**
- First line: imperative, ≤50 chars (e.g., "fix: add bounds check to loop")
- Blank line
- Body: explain motivation, context, trade-offs (wrap at 72 chars)
## Removing Files
When deleting files, Git needs to know if you want to:
1. Delete from both filesystem and version history
2. Stop tracking but keep the file locally
```bash
# Remove file from both filesystem and git history
# This deletion will be committed and propagated to remotes
git rm <file>
# Remove from git only (keep file)
# Useful when you want to stop tracking but keep locally
git rm --cached <file>
# Remove directory
git rm -r <directory>
Working with Remotes¶
Remotes are named references to repository copies on other machines (usually a server like GitLab, GitHub, or a shared filesystem).
Why use remotes?
Backup: Remote copy survives local disk failure
Collaboration: Team members share changes via remote
Distribution: Deploy code to build/test servers
Add Remote¶
Associate a local repository with a remote server. The name origin is conventional for the primary remote.
git remote add origin <repository-url>
View existing remotes:
git remote -v
Pull Changes¶
Retrieve changes from a remote repository and merge them into your current branch.
Why pull before working? Ensures you’re building on top of the latest code, avoiding conflicts later.
How pull works: git pull = git fetch (download) + git merge (integrate)
# Download remote changes and merge into current branch
git pull
# Pull with fast-forward only (safer)
# Fails if merge would create a merge commit
git pull --ff
Push Changes¶
Upload your local commits to a remote repository.
Why push? Share your work with teammates or deploy to a CI/CD pipeline.
How push works: Git compares local and remote histories, then transfers missing commits.
# Upload local commits to the configured remote branch
git push
# Push to specific branch
git push origin main
Note
If the remote has changes your local repo doesn’t have, you must git pull before git push.
Why pull first? Prevents lost work. If you push without syncing, Git rejects the push to protect remote history. Resolve conflicts locally before sharing.
Common Workflows¶
Daily Development¶
Standard workflow for contributing to a shared repository.
# Begin by syncing with remote to get teammates' changes
# This minimizes merge conflicts later
git pull
# Make your changes in the working directory
# Edit files, add features, fix bugs...
# Stage all modified files for commit
git add .
# Create a commit with a descriptive message
git commit -m "Feature X implemented"
# End of day: push changes
git push
Fixing a Mistake¶
Git provides safety nets for common errors. Choose the reset mode based on what you want to preserve.
# Undo last commit but preserve changes in working directory
# Changes remain staged, ready to recommit with correction
git reset --soft HEAD~1
# Undo last commit and discard all changes
# WARNING: This permanently deletes uncommitted work
git reset --hard HEAD~1
# Remove a file from staging area only
# File remains modified in working directory
git reset <file>
Understanding reset modes:
--soft: Move branch pointer only (changes stay staged)--mixed(default): Move pointer, unstaging changes (changes stay in working directory)--hard: Move pointer, discard all changes (DANGEROUS)
Viewing History¶
Inspect the commit log to understand project evolution or find when a bug was introduced.
# Show commit log
git log
# Compact one-line view
git log --oneline
# Show changes in last commit
git show
Log formatting tips:
git log --oneline --graph --all: Visual branch structuregit log -p: Show diffs with each commitgit log --author="name": Filter by contributorgit log --since="2 weeks ago": Time-based filtering
Best Practices¶
Commit often: Small, focused commits are easier to review, revert, and cherry-pick
Write clear messages: Explain why the change exists; code shows what
Pull before push: Always sync with remote first to minimize conflicts
Review before committing: Run
git statusandgit diff --stagedto verify changesUse
.gitignore: Exclude build artifacts, dependencies, and secrets to keep repos cleanUse branches: Feature branches isolate work and enable safe experimentation
Fetch regularly:
git fetchupdates remote refs without merging, letting you review before integrating