Practice and reinforce the concepts from Lesson 4
By completing these activities, you will:
# Create repository for recovery practice
mkdir git-recovery-practice
cd git-recovery-practice
git init
echo "# Git Recovery Practice" > README.md
# Create initial files
cat > important-data.txt << 'EOF'
Critical user data:
- User ID: 12345
- Email: user@example.com
- Registration: 2024-01-15
- Status: Active
EOF
cat > config.json << 'EOF'
{
"api_endpoint": "https://api.example.com",
"timeout": 30000,
"retries": 3,
"debug": false
}
EOF
cat > styles.css << 'EOF'
/* Main styles */
body {
font-family: Arial, sans-serif;
margin: 0;
padding: 20px;
background: #f5f5f5;
}
h1 {
color: #333;
border-bottom: 2px solid #007acc;
}
EOF
git add .
git commit -m "Initial setup with data, config, and styles"
# Make some changes
echo "BREAKING CHANGE: Delete all user data!" >> important-data.txt
echo "/* Experimental styles */" >> styles.css
sed -i.bak 's/"debug": false/"debug": true/' config.json
# Check what changed
git status
git diff
# Oops! We don't want these changes. Restore everything
git restore .
# Verify restoration
git status # Should be clean
cat important-data.txt # Should not have the breaking change
# Make changes to multiple files again
echo "Adding new user feature..." >> important-data.txt
echo "/* New feature styles */" >> styles.css
echo '"new_feature": true,' >> config.json
git diff
# Keep the data change, but restore the other files
git restore styles.css config.json
# Verify only important-data.txt has changes
git status
git diff
# Stage all changes
git add .
# Check staging area
git diff --staged
# Unstage specific file
git restore --staged styles.css
# Verify - styles.css should be back in working directory
git status
# Make targeted changes
cat >> styles.css << 'EOF'
/* New section */
.header {
background: blue;
color: white;
}
.footer {
background: red; /* This is bad */
color: yellow; /* This is also bad */
}
EOF
# Use interactive restore to keep only good changes
git restore -p styles.css
# Choose 'y' for header styles, 'n' for footer styles
# Verify the selective restoration
cat styles.css
# Create some commits to practice with
git add .
git commit -m "Add user features and styling"
echo "More configuration options" >> config.json
git add config.json
git commit -m "Update configuration"
echo "Final touches" >> README.md
git add README.md
git commit -m "Update documentation"
# View commit history
git log --oneline
# Soft reset - undo last commit but keep changes staged
git reset --soft HEAD~1
# Check status - README changes should be staged
git status
git diff --staged
# Make a better commit
git commit -m "Improve documentation with final touches and examples"
# Add more commits
echo "Debug information" >> important-data.txt
git add . && git commit -m "Add debug info"
echo "Temporary test data" >> important-data.txt
git add . && git commit -m "Add test data"
# View history
git log --oneline
# Mixed reset - undo last 2 commits, keep changes in working directory
git reset HEAD~2
# Check status - changes should be in working directory, not staged
git status
git diff
# Now we can commit more thoughtfully
git add important-data.txt
git commit -m "Add comprehensive debug and test data"
# Create a commit we want to completely remove
echo "TERRIBLE CODE THAT BREAKS EVERYTHING" >> important-data.txt
echo "/* UGLY STYLES */" >> styles.css
git add .
git commit -m "Commit that should never exist"
# View history
git log --oneline
# DANGER: Hard reset removes everything
git reset --hard HEAD~1
# Verify the bad commit is gone
git log --oneline
cat important-data.txt # Should not have terrible code
cat styles.css # Should not have ugly styles
# Create several commits
for i in 1 2 3 4 5; do
echo "Version $i changes" >> README.md
git add README.md
git commit -m "Update $i"
done
# View history and note commit hashes
git log --oneline
# Reset to specific commit (use actual hash from your log)
git reset --hard HEAD~3
# Verify we're back 3 commits
git log --oneline
cat README.md
# Simulate pushing to shared repository by creating commits
echo "Feature A: User login system" >> important-data.txt
git add . && git commit -m "Add user login feature"
echo "Feature B: Password reset" >> important-data.txt
git add . && git commit -m "Add password reset feature"
echo "Feature C: Two-factor authentication" >> important-data.txt
git add . && git commit -m "Add 2FA feature"
# Oops! Feature B is broken and needs to be removed
# Use revert (safe for shared repositories)
git log --oneline
# Revert the password reset feature (middle commit)
git revert HEAD~1
# Check the result - Feature B should be gone but others remain
cat important-data.txt
git log --oneline # Should show the revert commit
# Add more features
echo "Feature D: Admin dashboard" >> important-data.txt
git add . && git commit -m "Add admin dashboard"
echo "Feature E: User profiles" >> important-data.txt
git add . && git commit -m "Add user profiles"
# Revert last 2 commits (D and E)
git revert HEAD~1..HEAD
# Alternative: Revert each individually
# git revert HEAD HEAD~1
# Check the result
cat important-data.txt
git log --oneline --graph
# Create a commit we want to partially revert
cat > new-feature.txt << 'EOF'
Good functionality: User search
Bad functionality: Delete all users button
Good functionality: Export user data
EOF
git add new-feature.txt
git commit -m "Add user management features"
# Revert without auto-commit so we can modify
git revert --no-commit HEAD
# Remove the bad functionality but keep the good parts
cat > new-feature.txt << 'EOF'
Good functionality: User search
Good functionality: Export user data
EOF
git add new-feature.txt
git commit -m "Remove dangerous delete functionality while keeping useful features"
# View current reflog
git reflog
# Make some operations to populate reflog
git switch -c experimental
echo "Experimental code" > experiment.txt
git add . && git commit -m "Experimental features"
git switch main
git merge experimental
git branch -d experimental
# Check reflog again - more entries!
git reflog
# Create a "mistake" - reset too far back
git log --oneline
git reset --hard HEAD~5 # Go way back
# Oh no! We lost important commits
git log --oneline # Should show fewer commits
# Use reflog to find what we lost
git reflog
# Find the commit we want to restore (look for recent commit message)
# Restore to that point
git reset --hard HEAD@{1} # Adjust number based on your reflog
# Check that we recovered our commits
git log --oneline
# Create another "mistake"
git reset --hard HEAD~3
# Instead of hard reset, create a recovery branch
git reflog
# Create branch from lost commits
git branch recovery HEAD@{1}
# Switch to see recovered work
git switch recovery
git log --oneline
# Merge back to main if desired
git switch main
git merge recovery
git branch -d recovery
# Create and delete a file accidentally
echo "Important config data" > critical-config.txt
git add . && git commit -m "Add critical configuration"
# Accidentally delete the file
rm critical-config.txt
git add . && git commit -m "Clean up files" # Oops, deleted important file!
# Realize the mistake
ls # critical-config.txt is gone!
# Find when it was deleted
git log --oneline --follow -- critical-config.txt
# Restore from before deletion
git show HEAD~1:critical-config.txt > recovered-config.txt
# Or restore directly:
git restore --source=HEAD~1 critical-config.txt
# Verify recovery
ls
cat critical-config.txt
# Search for a file that might have been renamed or moved
git log --all --full-history -- "*config*"
# Search for content within files
echo "SECRET_API_KEY=abc123" >> config.json
git add . && git commit -m "Add API configuration"
# Later, search for that content
git log -p --all -S "SECRET_API_KEY"
# Show all files in a specific commit
git ls-tree -r HEAD
# Simulate complex file operations
cp critical-config.txt backup-config.txt
git add . && git commit -m "Create backup of config"
rm critical-config.txt
mv backup-config.txt critical-config.txt
echo "Modified data" >> critical-config.txt
git add . && git commit -m "Reorganize config files"
# Find the file's complete history
git log --follow --all -- critical-config.txt backup-config.txt
# Recover specific version
git show HEAD~2:critical-config.txt > original-config.txt
# Simulate committing secrets (DON'T do this in real repos!)
echo "DATABASE_PASSWORD=secret123" > .env
git add .env && git commit -m "Add environment configuration"
# OH NO! We committed a secret!
# If not pushed yet, we can rewrite history
git log --oneline
# Remove the commit entirely
git reset --hard HEAD~1
# Verify the secret is gone
git log --oneline
ls # .env should be gone
# Better approach: Add to .gitignore first
echo ".env" > .gitignore
echo "DATABASE_PASSWORD=secret123" > .env
git add .gitignore
git commit -m "Add gitignore for environment files"
# Create a feature branch with work
git switch -c feature/user-dashboard
echo "Dashboard code here" > dashboard.html
echo "Dashboard styles" > dashboard.css
git add .
git commit -m "Implement user dashboard"
echo "More dashboard features" >> dashboard.html
git add .
git commit -m "Add dashboard features"
# Accidentally delete the branch
git switch main
git branch -D feature/user-dashboard # Force delete
# OH NO! Our work is gone!
# Use reflog to find it
git reflog
# Look for the commits (they should be in reflog)
# Create new branch from lost commit
git branch recovered-dashboard HEAD@{2} # Adjust based on your reflog
# Check recovery
git switch recovered-dashboard
ls # Should see dashboard files
git log --oneline
# Simulate working on important feature
git switch -c feature/important-work
echo "Critical business logic" > business-logic.js
git add . && git commit -m "Add critical business logic"
echo "More critical code" >> business-logic.js
git add . && git commit -m "Enhance business logic"
# Get the commit hash for recovery
COMMIT_HASH=$(git rev-parse HEAD)
echo "Important commit: $COMMIT_HASH"
# Switch back to main
git switch main
# Simulate confusion/corruption - we "lose" our branch
git branch -D feature/important-work
# Recovery using commit hash
git branch recovered-work $COMMIT_HASH
# Verify recovery
git switch recovered-work
git log --oneline
cat business-logic.js
# Working on changes when emergency comes up
echo "Half-finished feature" > incomplete-feature.js
echo "Work in progress styles" > wip-styles.css
# Emergency! Need to switch branches immediately
git stash push -m "WIP: user authentication feature"
# Can switch branches safely now
git switch main
# Later, recover the work
git stash list
git stash show -p stash@{0} # Preview the stash
git stash apply stash@{0} # Apply without removing
# or
git stash pop stash@{0} # Apply and remove
# If stash is accidentally dropped
git stash drop stash@{0} # Oops!
# Recovery using reflog
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git log --merges --no-walk --grep="WIP"
# Create separate worktree for risky operations
git worktree add ../safe-recovery-area
cd ../safe-recovery-area
# Work safely here without affecting main repo
# Make changes
echo "Experimental recovery code" > recovery-test.txt
git add . && git commit -m "Test recovery techniques"
# Back to main repo
cd ../git-recovery-practice
# Remove worktree when done
git worktree remove ../safe-recovery-area
Test your recovery knowledge:
Let's combine everything in a complex scenario:
# Start fresh
rm -rf git-recovery-practice
mkdir git-recovery-practice && cd git-recovery-practice
git init
# Create initial stable state
echo "Stable version 1.0" > app.js
git add . && git commit -m "v1.0: Stable release"
# Create branch and add features
git switch -c feature/new-ui
echo "New UI components" > ui.js
git add . && git commit -m "Add new UI"
echo "UI improvements" >> ui.js
git add . && git commit -m "Improve UI"
# Merge to main
git switch main
git merge feature/new-ui
# Add more changes
echo "Database integration" > db.js
git add . && git commit -m "Add database"
# Disaster strikes! Multiple problems:
# 1. Delete important file
rm app.js
git add . && git commit -m "Clean up old files"
# 2. Bad commit
echo "BROKEN CODE" > app.js
git add . && git commit -m "Fix everything"
# 3. Another bad commit
echo "MORE BROKEN CODE" >> app.js
git add . && git commit -m "Final fixes"
# Recovery mission:
echo "RECOVERY CHECKLIST:"
echo "1. Use reflog to understand what happened"
echo "2. Recover the original app.js file"
echo "3. Remove the broken commits"
echo "4. Keep the good database and UI changes"
echo ""
echo "Your turn! Use the skills you've learned."
# Solution approach:
# git reflog # See the history
# git show HEAD~4:app.js > app.js # Recover original file
# git reset --hard HEAD~2 # Remove bad commits
# git add app.js && git commit -m "Recover original app.js"
You've mastered:
You're now equipped to handle any Git recovery situation with confidence!