Git's power lies not just in tracking your code changes, but also in giving you precise control over what gets tracked and how to manage those changes. Two essential skills every developer needs are understanding how to reset changes with git reset
and controlling what Git ignores with .gitignore
files.
🎯 What You'll Learn: In this comprehensive Git tutorial, you'll master:
- Understanding Git's staging area and working directory
- The difference between
git reset
andgit reset --hard
- How to unstage files without losing changes
- How to discard changes completely with reset --hard
- Creating and using .gitignore files effectively
- Common .gitignore patterns for different file types
- Managing directories and file extensions with .gitignore
- Best practices for repository cleanliness
🏗️ Understanding Git's Three Areas
Before diving into reset commands, let's understand how Git organizes your files:
The Three Key Areas
- Working Directory: Your actual files that you edit
- Staging Area: Files prepared for the next commit
- Repository: Committed snapshots of your project
Area | Purpose | Commands That Affect It |
---|---|---|
Working Directory | Files you're currently editing | git reset --hard , file edits |
Staging Area | Files ready for next commit | git add , git reset |
Repository | Permanent project history | git commit |
🔄 Understanding Git Status Output
Let's start by understanding what git status
tells us. This command shows the current state of our working directory and staging area.
git status
Sample Output:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: second
no changes added to commit (use "git add" and/or "git commit -a")
Understanding the Output:
- "On branch master": Shows which branch you're currently on
- "Changes not staged for commit": Files that are modified but not yet added to staging area
- "modified: second": The file
second
has been changed since the last commit - "no changes added to commit": Nothing is currently staged for commit
✅ Git Status: This is your most important command for understanding what Git sees. Always run git status
before making any changes to understand your current state.
📝 Making Changes and Staging Files
Let's see how changes move through Git's three areas:
nano first.txt
Add some content:
hello
working
new changes
git status
Output:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: first.txt
modified: second
no changes added to commit (use "git add" and/or "git commit -a")
What This Shows:
- Both
first.txt
andsecond
have been modified - Changes exist in working directory but not in staging area
- Git suggests using
git add
to stage changes orgit restore
to discard them
Now let's stage the changes:
git add .
git status
Output:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: first.txt
modified: second
What Changed:
- Files moved from "Changes not staged" to "Changes to be committed"
- Both files are now in the staging area
- Git suggests using
git restore --staged
to unstage files
🔄 Git Reset: Unstaging Changes
The basic git reset
command moves files from the staging area back to the working directory without deleting your changes.
git reset
Output:
Unstaged changes after reset:
M first.txt
M second
git status
Output:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: first.txt
modified: second
no changes added to commit (use "git add" and/or "git commit -a")
✅ Git Reset Behavior: The basic git reset
command only affects the staging area. Your working directory changes remain intact. This is safe - you won't lose any work.
Before git reset | After git reset |
---|---|
Files in staging area (ready to commit) | Files back in working directory (not staged) |
Your file changes are preserved | Your file changes are still preserved |
⚠️ Git Reset --Hard: Discarding Changes Completely
The git reset --hard
command is more powerful and dangerous - it discards changes in both the staging area AND the working directory.
Let's stage our changes again first:
git add .
git status
Output:
On branch master
Changes to be committed:
(use "git restore --staged <file>..." to unstage)
modified: first.txt
modified: second
Now let's use the hard reset:
git reset --hard
Output:
HEAD is now at 4d0a85e update first.txt
git status
Output:
On branch master
nothing to commit, working tree clean
Let's verify our changes are gone:
cat first.txt
Output:
hello
working
The "new changes" line we added is gone! Let's check the other file:
cat second
Output: (Empty file - no content)
⚠️ Warning: git reset --hard
permanently deletes changes that aren't committed. Use this command carefully! Always make sure you really want to discard your work before using it.
🔄 Demonstrating the Difference
Let's clearly demonstrate the difference between git reset
and git reset --hard
:
Test 1: Regular Git Reset (Safe)
nano second
Add content:
hello
git status
Output:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: second
no changes added to commit (use "git add" and/or "git commit -a")
git reset
Output:
Unstaged changes after reset:
M second
Since nothing was staged, the reset had no effect:
git status
Output:
On branch master
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
modified: second
no changes added to commit (use "git add" and/or "git commit -a")
Test 2: Hard Reset (Destructive)
git reset --hard
Output:
HEAD is now at 4d0a85e update first.txt
cat second
Output: (Empty file - our "hello" content is gone)
Command | Staging Area | Working Directory | Safety |
---|---|---|---|
git reset | Cleared | Unchanged | ✅ Safe |
git reset --hard | Cleared | Reset to last commit | ⚠️ Destructive |
📁 Introduction to .gitignore
The .gitignore
file tells Git which files and directories to ignore. This is crucial for keeping your repository clean by excluding:
- Temporary files
- Build artifacts
- Environment-specific files
- Sensitive information
- IDE configuration files
Let's start with a practical example:
git status
Output:
On branch master
nothing to commit, working tree clean
touch third.txt
nano third.txt
Content:
this is a new file
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
third.txt
nothing added to commit but untracked files present (use "git add" to track)
Understanding "Untracked files":
- Git sees
third.txt
but isn't tracking it yet - The file exists in your working directory but Git doesn't know about it
- You need to
git add
it to start tracking it
🚫 Creating Your First .gitignore File
touch .gitignore
ls -al
Output:
total 8
drwxr-xr-x. 3 centos9 centos9 84 Sep 23 12:47 .
drwxr-xr-x. 3 centos9 centos9 23 Sep 22 16:44 ..
-rw-r--r--. 1 centos9 centos9 15 Sep 23 12:41 first.txt
drwxr-xr-x. 8 centos9 centos9 183 Sep 23 12:46 .git
-rw-r--r--. 1 centos9 centos9 0 Sep 23 12:47 .gitignore
-rw-r--r--. 1 centos9 centos9 0 Sep 23 12:39 second
-rw-r--r--. 1 centos9 centos9 19 Sep 23 12:46 third.txt
Notice the .gitignore
file:
- It starts with a dot, making it a hidden file
- Use
ls -al
to see hidden files (the-a
flag shows all files) - The
.git
directory is also hidden
nano .gitignore
Content of .gitignore:
third.txt
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
What happened:
third.txt
is no longer shown as untracked- Git is now ignoring
third.txt
completely - Only
.gitignore
itself is shown as untracked - The
.gitignore
file is not ignored by default
✅ Gitignore Effect: Once a file is listed in .gitignore
, Git acts as if it doesn't exist. The file is still there physically, but Git won't track it or show it in status.
🎯 Gitignore Patterns: Wildcard Matching
Ignoring File Extensions
Instead of ignoring individual files, you can ignore entire file types using patterns:
touch fourth.txt
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
fourth.txt
nothing added to commit but untracked files present (use "git add" to track)
nano .gitignore
New content:
*.txt
Understanding the *
wildcard:
*
matches any characters*.txt
means "any file ending with .txt"- This will ignore
third.txt
,fourth.txt
, and any future.txt
files
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
Result:
- Both
third.txt
andfourth.txt
are now ignored - Only
.gitignore
is still shown as untracked
Testing with Different File Types
touch test1.xlsx
touch test2.xlsx
ls -al
Output:
total 12
drwxr-xr-x. 3 centos9 centos9 138 Sep 23 13:01 .
drwxr-xr-x. 3 centos9 centos9 23 Sep 22 16:44 ..
-rw-r--r--. 1 centos9 centos9 15 Sep 23 12:41 first.txt
-rw-r--r--. 1 centos9 centos9 0 Sep 23 12:49 fourth.txt
drwxr-xr-x. 8 centos9 centos9 183 Sep 23 13:00 .git
-rw-r--r--. 1 centos9 centos9 6 Sep 23 12:50 .gitignore
-rw-r--r--. 1 centos9 centos9 0 Sep 23 12:39 second
-rw-r--r--. 1 centos9 centos9 0 Sep 23 13:01 test1.xlsx
-rw-r--r--. 1 centos9 centos9 0 Sep 23 13:01 test2.xlsx
-rw-r--r--. 1 centos9 centos9 19 Sep 23 12:46 third.txt
nano .gitignore
Updated content:
*.txt
*.xlsx
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
What we learned:
- You can have multiple patterns in
.gitignore
- Each pattern goes on its own line
- Empty lines are allowed for organization
- Both
.txt
and.xlsx
files are now ignored
Testing Pattern Removal
Let's see what happens when we remove a pattern:
nano .gitignore
Content after removing Excel pattern:
*.txt
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
test1.xlsx
test2.xlsx
nothing added to commit but untracked files present (use "git add" to track)
Result:
.txt
files are still ignored.xlsx
files are now visible to Git again- This shows that
.gitignore
changes take effect immediately
nano .gitignore
Final content:
*.txt
*.xlsx
📂 Ignoring Directories
You can also ignore entire directories and their contents:
mkdir build
cd build/
touch hello.txt
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
../.gitignore
nothing added to commit but untracked files present (use "git add" to track)
Understanding the output:
- Git shows
../.gitignore
(the parent directory's .gitignore file) - The
hello.txt
file isn't shown because*.txt
is ignored - But the build directory itself would be tracked if it had non-ignored files
cd ..
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
nothing added to commit but untracked files present (use "git add" to track)
Now let's test with a specific file ignored instead of the pattern:
nano .gitignore
Content:
third.txt
*.xlsx
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
build/
fourth.txt
nothing added to commit but untracked files present (use "git add" to track)
What changed:
third.txt
is still ignored (specific file)fourth.txt
is now visible (*.txt pattern removed)build/
directory is visible because it contains a.txt
file that's not ignored by the specific pattern
Let's check what's in the build directory from Git's perspective:
cd build/
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
../.gitignore
./
../fourth.txt
nothing added to commit but untracked files present (use "git add" to track)
Understanding the paths:
../.gitignore
: The .gitignore file in parent directory./
: The current directory (build/)../fourth.txt
: The fourth.txt file in parent directory
Adding Directory to .gitignore
cd ..
nano .gitignore
Content:
third.txt
*.xlsx
build
git status
Output:
On branch master
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
fourth.txt
nothing added to commit but untracked files present (use "git add" to track)
Result:
build/
directory is no longer shown- Git ignores the entire directory and everything inside it
fourth.txt
is still visible because it's not covered by our ignore patterns
✅ Directory Ignoring: When you add a directory name to .gitignore
, Git ignores the entire directory and all its contents, regardless of what files are inside.
🎯 Common .gitignore Patterns
Pattern | What It Ignores | Example |
---|---|---|
filename.txt | Specific file | config.local.json |
*.txt | All files with .txt extension | notes.txt, readme.txt |
build | Directory named "build" | build/ and all contents |
build/ | Only directories named "build" | build/ but not build.txt |
/logs | logs directory at root only | ./logs but not src/logs |
**/logs | logs directory anywhere | logs, src/logs, app/logs |
logs/** | Everything inside logs directory | All files and subdirs in logs/ |
🛠️ Real-World .gitignore Examples
Node.js Project
# Dependencies
node_modules/
npm-debug.log*
# Environment variables
.env
.env.local
# Build output
dist/
build/
# IDE files
.vscode/
.idea/
# OS files
.DS_Store
Thumbs.db
Python Project
# Byte-compiled / optimized files
__pycache__/
*.py[cod]
*$py.class
# Virtual environments
venv/
env/
.venv/
# IDE
.vscode/
.idea/
# Build
build/
dist/
*.egg-info/
General Development
# Logs
logs/
*.log
# Temporary files
*.tmp
*.temp
.cache/
# Backups
*.bak
*.backup
# IDE and editor files
.vscode/
.idea/
*.swp
*.swo
# OS generated files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db
⚠️ Important .gitignore Rules
1. .gitignore Doesn't Affect Already Tracked Files
⚠️ Important: If a file is already being tracked by Git, adding it to .gitignore
won't stop tracking it. You need to untrack it first with git rm --cached filename
.
2. .gitignore Should Be Committed
git add .gitignore
git commit -m "Add .gitignore file"
The .gitignore
file should be tracked so that all team members use the same ignore rules.
3. Global .gitignore
You can create a global .gitignore
file for patterns you want to ignore across all your projects:
git config --global core.excludesfile ~/.gitignore_global
🎯 Best Practices
Practice | Why | Example |
---|---|---|
Add .gitignore early | Prevents accidentally committing unwanted files | Create .gitignore with first commit |
Use comments | Makes .gitignore file easier to understand | # Node.js dependencies |
Group related patterns | Organizes the file logically | Group IDE files together |
Never ignore .gitignore | Team needs to share ignore rules | Always commit .gitignore file |
🚨 Common Mistakes to Avoid
1. Using git reset --hard
Without Thinking
git reset --hard # This deletes your changes permanently!
Better approach:
git status # Check what you have first
git stash # Save changes temporarily
# or
git add . # Stage changes
git commit -m "WIP: work in progress" # Save changes
2. Ignoring Files That Are Already Tracked
echo "config.json" >> .gitignore
Correct approach:
git rm --cached config.json # Remove from tracking
echo "config.json" >> .gitignore # Then ignore
git commit -m "Remove config.json from tracking"
3. Not Testing .gitignore Patterns
git status # Check if files are properly ignored
🎯 Key Takeaways
✅ Remember These Points
- git reset: Safely moves files from staging area to working directory
- git reset --hard: Permanently discards changes in both staging and working directory
- .gitignore: Controls which files Git ignores completely
- Patterns: Use
*
for wildcards, directory names to ignore folders - Safety: Always check
git status
before using reset commands
📋 Git Reset and .gitignore Command Cheat Sheet
Command | Purpose | Safety |
---|---|---|
git status | Check current state of files | ✅ Always safe |
git add . | Stage all changes for commit | ✅ Safe |
git reset | Unstage files (keep changes) | ✅ Safe |
git reset --hard | Discard all changes permanently | ⚠️ Destructive |
touch .gitignore | Create .gitignore file | ✅ Safe |
nano .gitignore | Edit .gitignore file | ✅ Safe |
ls -al | List all files (including hidden) | ✅ Safe |
Common .gitignore Patterns Cheat Sheet
Pattern | Ignores |
---|---|
filename.txt | Specific file |
*.txt | All .txt files |
build | build directory |
*.log | All log files |
node_modules/ | Node.js dependencies |
.env | Environment variables |
🎉 Congratulations! You've mastered Git reset commands and .gitignore files. You now know how to safely manage your staging area, discard changes when needed, and keep your repository clean by ignoring unwanted files. These skills are essential for professional Git workflow management.