Skip to content

rm Git Command Guide

The git rm command removes files from the working tree and/or the index (staging area). It can remove files completely from Git tracking or just unstage them while keeping the working tree copy.

Terminal window
git rm [-f | --force] [-n] [-r] [--cached] [--ignore-unmatch]
[--quiet] [--pathspec-from-file=<file> [--pathspec-file-nul]]
[--] [<pathspec>...]
OptionDescription
-f, --forceOverride safety checks, remove modified files
-n, --dry-runShow what would be removed without doing it
-rAllow recursive removal of directories
--cachedRemove from index only, keep working tree file
OptionDescription
--ignore-unmatchExit successfully if no files match
-q, --quietSuppress output messages
--pathspec-from-file=<file>Read pathspecs from file
--pathspec-file-nulUse NUL character as path separator
ParameterDescription
<pathspec>Files/directories to remove (globbing supported)
File Removal Types:
├── Complete Removal: Remove from working tree AND index
├── Index-Only Removal: Remove from index, keep working tree
├── Recursive Removal: Remove directories and contents
├── Forced Removal: Remove modified/staged files
Safety Checks:
├── File must match HEAD version (unless -f used)
├── No staged changes allowed (unless -f used)
├── Sparse checkout patterns respected
└── Only Git-tracked files can be removed
Pathspec Patterns:
├── file.txt # Single file
├── *.txt # All .txt files
├── dir/ # Directory (requires -r)
├── dir/*.js # JS files in directory
├── **/*.log # Log files recursively
Globbing Behavior:
├── Matches across directory boundaries
├── d* vs d/* have different meanings
├── Hidden files need explicit specification
└── Untracked files are ignored
Removal Workflow:
1. Validate pathspecs match Git-tracked files
2. Check safety conditions (unless -f)
3. Remove from index (always)
4. Remove from working tree (unless --cached)
5. Stage the removal for commit
6. Update Git's internal state
Terminal window
# Remove a single file
git rm file.txt
# Remove multiple files
git rm file1.txt file2.txt file3.txt
# Remove with confirmation
git rm -n file.txt # Dry run first
git rm file.txt # Then remove
Terminal window
# Remove directory recursively
git rm -r directory/
# Remove multiple directories
git rm -r dir1/ dir2/
# Remove directory contents only
git rm directory/* # Keeps directory itself
Terminal window
# Unstage file but keep in working tree
git rm --cached file.txt
# Unstage entire directory
git rm --cached -r directory/
# Unstage all files matching pattern
git rm --cached *.log
Terminal window
# Remove modified file
git rm -f modified-file.txt
# Remove staged but modified file
git add file.txt # Stage changes
# Modify file.txt again
git rm -f file.txt # Force remove despite staged changes
# Remove files that differ from HEAD
git rm -f file-with-changes.txt
Terminal window
# Remove all log files
git rm *.log
# Remove files recursively by pattern
git rm "**/*.tmp"
# Remove files in specific directories
git rm "src/**/*.bak"
# Remove files matching multiple patterns
git rm *.txt *.md *.html
Terminal window
# Remove files listed in a file
echo "file1.txt" > to-remove.txt
echo "file2.txt" >> to-remove.txt
git rm --pathspec-from-file=to-remove.txt
# Remove files with null separators
printf "file1.txt\0file2.txt\0" | git rm --pathspec-from-file=- --pathspec-file-nul
# Remove files from find command
find . -name "*.tmp" -print0 | git rm --pathspec-from-file=- --pathspec-file-nul
Terminal window
# Remove directory but keep specific files
git rm -r directory/
# This removes everything in directory/
# Remove all except specific files (requires manual approach)
git rm directory/file1.txt directory/file2.txt
# Keep directory/file3.txt
Terminal window
# Configure rm behavior
git config core.sparseCheckout true # Enable sparse checkout
# Configure safety checks
git config core.trustctime false # Don't trust file timestamps
# Configure pathspec handling
git config core.quotePath true # Quote paths with special characters
Terminal window
# Always check what will be removed
git rm -n <pathspec> # Dry run first
# Backup before mass removal
git branch backup-before-rm
# Verify files are tracked
git ls-files <pathspec> # Check what's tracked
# Use git status to confirm changes
git status # After removal
Terminal window
# Recover accidentally removed files
git checkout HEAD -- <removed-file> # Restore from HEAD
git reset HEAD <removed-file> # Unstage removal
git checkout <removed-file> # Restore working tree
# Recover directory
git checkout HEAD -- <directory>/
#!/bin/bash
# Repository cleanup workflow
cleanup_repository() {
echo "Cleaning up repository..."
# Remove build artifacts
git rm -f --ignore-unmatch *.o *.exe *.dll 2>/dev/null || true
# Remove temporary files
git rm -f --ignore-unmatch *.tmp *.temp 2>/dev/null || true
# Remove log files
git rm -f --ignore-unmatch *.log 2>/dev/null || true
# Remove IDE files
git rm -f --ignore-unmatch .vscode/settings.json 2>/dev/null || true
git rm -f --ignore-unmatch *.swp *.swo 2>/dev/null || true
echo "Cleanup complete. Review changes with: git status"
}
cleanup_repository
Terminal window
# Use rm --cached for selective staging
selective_stage() {
local file="$1"
# Stage everything
git add .
# Unstage the specific file (keeps changes)
git rm --cached "$file"
echo "File $file unstaged but changes preserved"
echo "Use: git add $file # to stage later"
echo "Or: git checkout $file # to discard changes"
}
selective_stage "config/secrets.txt"
Terminal window
# Restructure repository with rm
restructure_repo() {
echo "Restructuring repository..."
# Move files to new structure
mkdir -p new-structure/
git mv old-file.txt new-structure/
# Remove old directories
git rm -r old-directory/
# Clean up empty directories
find . -type d -empty -delete 2>/dev/null || true
echo "Repository restructured"
}
restructure_repo
Terminal window
# Handle modified file removal
git status # Check modified files
# Option 1: Commit changes first
git add modified-file.txt
git commit -m "Update file before removal"
# Option 2: Force removal (loses changes)
git rm -f modified-file.txt
# Option 3: Stash changes, remove, then apply stash
git stash push modified-file.txt
git rm modified-file.txt
git stash pop # May cause conflicts
Terminal window
# Fix pathspec problems
git rm "file with spaces.txt" # Quote paths with spaces
# Use -- to separate paths from options
git rm -- -file.txt # Remove file named "-file.txt"
# Handle globbing issues
git rm "*.txt" # Remove all .txt files
git rm "dir/*.txt" # Remove .txt files in dir/
Terminal window
# Handle directory removal
git rm directory/ # Fails without -r
# Remove directory recursively
git rm -r directory/
# Remove directory contents but keep directory
git rm directory/* # Directory remains empty
Terminal window
# Handle sparse checkout issues
git sparse-checkout list # Check patterns
# Remove files outside sparse patterns
git rm --ignore-unmatch outside-file.txt
# Disable sparse checkout temporarily
git sparse-checkout disable
git rm problematic-file.txt
git sparse-checkout reapply
Terminal window
# Recover from accidental removal
git reflog # Find last good state
# Restore from reflog
git reset --hard HEAD@{1} # Go back one step
# Restore specific file
git checkout HEAD@{1} -- accidentally-removed.txt
# Unstage removal
git reset HEAD accidentally-removed.txt
Terminal window
# Handle large file removals efficiently
large_removal() {
local pattern="$1"
echo "Removing files matching: $pattern"
# Use find for large operations
find . -name "$pattern" -type f | head -100 | while read -r file; do
git rm "$file" 2>/dev/null || true
done
# Commit in batches
git commit -m "Remove batch of $pattern files"
}
large_removal "*.log"
#!/bin/bash
# Comprehensive repository cleanup
cleanup_repo() {
echo "Starting repository cleanup..."
# Remove common build artifacts
artifacts=("*.o" "*.exe" "*.dll" "*.so" "*.dylib" "*.class" "*.jar")
for pattern in "${artifacts[@]}"; do
git rm -f --ignore-unmatch "$pattern" 2>/dev/null || true
done
# Remove temporary files
temp_files=("*.tmp" "*.temp" "*~" "*.swp" "*.swo" ".DS_Store")
for pattern in "${temp_files[@]}"; do
git rm -f --ignore-unmatch "$pattern" 2>/dev/null || true
done
# Remove log files
git rm -f --ignore-unmatch "*.log" "logs/*.log" 2>/dev/null || true
# Remove IDE files
ide_files=(".vscode/" ".idea/" "*.sublime-*" ".atom/")
for dir in "${ide_files[@]}"; do
git rm -r -f --ignore-unmatch "$dir" 2>/dev/null || true
done
# Remove OS-specific files
os_files=("Thumbs.db" "Desktop.ini" ".AppleDouble/")
for file in "${os_files[@]}"; do
git rm -f --ignore-unmatch "$file" 2>/dev/null || true
done
echo "Cleanup complete. Run: git status"
}
cleanup_repo
Terminal window
# Manage sensitive files
manage_sensitive_files() {
echo "Managing sensitive files..."
# Remove sensitive files from tracking
sensitive_files=("config/secrets.json" "keys/private.pem" "*.key")
for file in "${sensitive_files[@]}"; do
if git ls-files "$file" | grep -q .; then
echo "Removing sensitive file: $file"
git rm -f "$file"
# Add to .gitignore if not already there
if ! grep -q "^$(basename "$file")$" .gitignore 2>/dev/null; then
echo "$(basename "$file")" >> .gitignore
git add .gitignore
fi
fi
done
echo "Sensitive files removed and .gitignore updated"
}
manage_sensitive_files
Terminal window
# Organize files by type
organize_by_type() {
echo "Organizing files by type..."
# Create type directories
mkdir -p docs/ scripts/ images/ data/
# Move files to appropriate directories
git mv *.md docs/ 2>/dev/null || true
git mv *.sh scripts/ 2>/dev/null || true
git mv *.png *.jpg *.gif images/ 2>/dev/null || true
git mv *.json *.csv *.xml data/ 2>/dev/null || true
# Remove empty directories
find . -type d -empty -delete 2>/dev/null || true
echo "Files organized by type"
}
organize_by_type
# Pre-commit hook for file validation
cat > .git/hooks/pre-commit << 'EOF'
#!/bin/bash
# Validate files before commit
# Remove unwanted files
unwanted_patterns=("*.log" "*.tmp" "*~" ".DS_Store")
for pattern in "${unwanted_patterns[@]}"; do
if git ls-files "$pattern" | grep -q .; then
echo "Removing unwanted files: $pattern"
git rm -f --ignore-unmatch "$pattern"
fi
done
# Check file sizes
git ls-files | while read -r file; do
size=$(wc -c < "$file")
if [ "$size" -gt 10485760 ]; then # 10MB
echo "File too large: $file (${size} bytes)"
echo "Consider using Git LFS for large files"
exit 1
fi
done
echo "✓ Pre-commit validation passed"
EOF
chmod +x .git/hooks/pre-commit
Terminal window
# Clean up during repository migration
migration_cleanup() {
local source_scm="$1"
echo "Cleaning up repository migrated from $source_scm..."
case "$source_scm" in
"svn")
# Remove SVN metadata
find . -name ".svn" -type d -exec git rm -r {} + 2>/dev/null || true
git rm -f --ignore-unmatch .git/svn/ 2>/dev/null || true
;;
"hg")
# Remove Mercurial metadata
git rm -f --ignore-unmatch .hg* 2>/dev/null || true
git rm -f --ignore-unmatch .hg/ 2>/dev/null || true
;;
"cvs")
# Remove CVS metadata
find . -name "CVS" -type d -exec git rm -r {} + 2>/dev/null || true
git rm -f --ignore-unmatch .cvsignore 2>/dev/null || true
;;
esac
# Remove common migration artifacts
git rm -f --ignore-unmatch *.orig *.rej 2>/dev/null || true
echo "Migration cleanup complete"
}
migration_cleanup "svn"
Terminal window
# Batch remove files based on criteria
batch_remove() {
local criteria="$1"
echo "Batch removing files by criteria: $criteria"
case "$criteria" in
"old")
# Remove files not modified in 6 months
find . -type f -mtime +180 | while read -r file; do
if git ls-files "$file" | grep -q .; then
git rm "$file" 2>/dev/null || true
fi
done
;;
"large")
# Remove files larger than 1MB
git ls-files | while read -r file; do
if [ -f "$file" ] && [ "$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file")" -gt 1048576 ]; then
git rm "$file"
fi
done
;;
"unused")
# Remove files not referenced in code
# This is complex and would require language-specific analysis
echo "Unused file detection requires manual analysis"
;;
esac
echo "Batch removal complete"
}
batch_remove "large"

What’s the difference between git rm and rm?

Section titled “What’s the difference between git rm and rm?”

git rm removes files from Git tracking and working tree; rm only removes from filesystem. git rm stages the removal for commit.

How do I remove a file from Git but keep it locally?

Section titled “How do I remove a file from Git but keep it locally?”

Use git rm —cached to remove from index while keeping the working tree copy.

Yes, use git rm -r to recursively remove directories and their contents.

What happens if I try to remove a modified file?

Section titled “What happens if I try to remove a modified file?”

Git prevents removal unless you use -f. The file must match the HEAD version or you must force removal.

Use globbing: git rm *.txt removes all .txt files. Use quotes for complex patterns.

Yes, git reset HEAD unstages the removal, and git checkout restores the working tree copy.

What’s the difference between git rm and git mv?

Section titled “What’s the difference between git rm and git mv?”

git rm removes files; git mv moves/renames files while preserving Git history.

How do I remove files from the repository but keep them in .gitignore?

Section titled “How do I remove files from the repository but keep them in .gitignore?”

Remove with git rm, then add to .gitignore to prevent future tracking.

No, git rm only works with tracked files. Use regular rm for untracked files.

How do I remove a file from all commits in history?

Section titled “How do I remove a file from all commits in history?”

Use git filter-branch or git filter-repo to rewrite history. git rm only affects the current state.

What’s the —ignore-unmatch option for?

Section titled “What’s the —ignore-unmatch option for?”

Makes git rm exit successfully even if no files match the pathspec, useful in scripts.

No, git rm removes files from tracking. Use git reset to unstage changes.

Use —pathspec-from-file= to read pathspecs from a file, one per line.

What’s the safest way to remove many files?

Section titled “What’s the safest way to remove many files?”

Use -n for dry run first, then remove. Consider doing it in batches for large operations.

Yes, but it only removes paths within the sparse checkout patterns.

How do I remove files that were accidentally added?

Section titled “How do I remove files that were accidentally added?”

Use git rm —cached to unstage them, then add proper ignore rules.

  1. File Cleanup: Remove unwanted files from repository tracking
  2. Repository Restructuring: Reorganize file structure and remove old directories
  3. Security Management: Remove sensitive files and update ignore rules
  4. Build Artifact Removal: Clean up generated files and build outputs
  5. Migration Cleanup: Remove old SCM metadata during repository migration
  6. Selective Staging: Unstage files while preserving working tree changes