Pre-Commit Hook: Handle Deleted Files Gracefully
@hmoreras is already working on this.
Since Dec 2, 2025.
- Dominant language
- Java
- Stars
- 970
- Forks
- 486
- Avg merge
- 3d 33m
- Merged PRs (30d)
- 170
Description
Pre-Commit Hook: Handle Deleted Files Gracefully
Problem Statement
The pre-commit hook (core-web/.husky/pre-commit) fails when attempting to restore files that have been deleted and staged for commit. This causes the entire commit process to fail with an error:
fatal: pathspec 'core-web/libs/sdk/analytics/src/lib/core/shared/dot-content-analytics.activity-tracker.spec.ts' did not match any files
Root Cause
The hook attempts to restore files using git restore in three locations:
- Line ~292: When restoring unmatched files that were modified by linting/formatting
- Line ~531: When backing up untracked files during the restore process
- Line ~542: When restoring untracked files after processing
When a file is deleted (marked with D in git status) and staged, git restore fails because the file no longer exists in the working directory. The hook doesn't check if a file is deleted before attempting to restore it.
Impact
- Developer Experience: Commits fail unexpectedly when files are deleted as part of refactoring or cleanup
- Workflow Disruption: Developers must bypass the hook with
--no-verifyto commit, which defeats the purpose of pre-commit checks - Error Messages: The error message is not clear about what's happening, making it difficult to diagnose
Example Scenario
When refactoring code and moving/renaming files:
# File is deleted and staged
git status
# D core-web/libs/sdk/analytics/src/lib/core/shared/dot-content-analytics.activity-tracker.spec.ts
# Attempting to commit triggers pre-commit hook
git commit -m "Refactor: Move analytics tracker to new location"
# ❌ fatal: pathspec '...' did not match any files
Proposed Solution
Update the pre-commit hook to detect deleted files before attempting to restore them. The fix involves:
- Check for deleted files first: Before calling
git restore, check if the file is marked as deleted (D) in staged changes - Skip restore for deleted files: If a file is deleted, skip the restore operation and log an informational message
- Handle non-existent files gracefully: If a file doesn't exist in the git index, skip restore operations
Implementation Details
Location 1: Unmatched Files Restoration (Line ~288-303)
Current Code:
for file in "${unmatched_files[@]}"; do
printf " %s\n" "${file}"
cd "$root_dir" || exit 1
git restore "${file}" # ❌ Fails for deleted files
cd "${root_dir}/core-web" || exit 1
done
Fixed Code:
for file in "${unmatched_files[@]}"; do
printf " %s\n" "${file}"
cd "$root_dir" || exit 1
# Check if file is deleted (D) in staged changes first
if git diff --cached --name-status | grep -q "^D[[:space:]].*${file}$"; then
# File is deleted, skip restore
print_color "$BLUE" " ℹ️ Skipping restore for deleted file: ${file}"
elif git ls-files --error-unmatch "${file}" >/dev/null 2>&1; then
# File exists in git index, safe to restore
git restore "${file}" 2>/dev/null || true
else
# File doesn't exist, skip restore
print_color "$BLUE" " ℹ️ Skipping restore for non-existent file: ${file}"
fi
cd "${root_dir}/core-web" || exit 1
done
Location 2: Untracked Files Backup (Line ~524-534)
Current Code:
for file in $untracked_files; do
if echo "${staged_files}" | grep -q "^${file}$"; then
mkdir -p "${temp_dir}/$(dirname "${file}")"
cp "${root_dir}/${file}" "${temp_dir}/${file}" # ❌ Fails if file doesn't exist
print_color "$BLUE" "💾 Backing up ${file}"
cd "$root_dir" || exit 1
git restore "${file}" # ❌ Fails for deleted files
cd "${root_dir}/core-web" || exit 1
fi
done
Fixed Code:
for file in $untracked_files; do
if echo "${staged_files}" | grep -q "^${file}$"; then
# Check if file exists before trying to backup
if [ -f "${root_dir}/${file}" ]; then
mkdir -p "${temp_dir}/$(dirname "${file}")"
cp "${root_dir}/${file}" "${temp_dir}/${file}"
print_color "$BLUE" "💾 Backing up ${file}"
cd "$root_dir" || exit 1
git restore "${file}" 2>/dev/null || true
cd "${root_dir}/core-web" || exit 1
else
print_color "$BLUE" "ℹ️ Skipping backup for non-existent file: ${file}"
fi
fi
done
Location 3: Second Untracked Files Restore (Line ~539-545)
Current Code:
for file in $untracked_files; do
if echo "${staged_files}" | grep -q "^${file}$"; then
cd "$root_dir" || exit 1
git restore "${file}" # ❌ Fails for deleted files
cd "${root_dir}/core-web" || exit 1
fi
done
Fixed Code:
for file in $untracked_files; do
if echo "${staged_files}" | grep -q "^${file}$"; then
# Only restore if file exists in git index
if git ls-files --error-unmatch "${file}" >/dev/null 2>&1; then
cd "$root_dir" || exit 1
git restore "${file}" 2>/dev/null || true
cd "${root_dir}/core-web" || exit 1
fi
fi
done
Benefits
- Robust Error Handling: The hook no longer fails when files are deleted
- Better Developer Experience: Developers can commit refactoring changes without bypassing hooks
- Clear Feedback: Informational messages explain why certain operations are skipped
- Maintains Functionality: All existing functionality remains intact for non-deleted files
Testing Scenarios
To verify the fix works correctly, test with:
- Deleted file in staged changes: Stage a file deletion and attempt to commit
- Mixed changes: Stage both file deletions and modifications together
- Renamed files: Stage file renames (which appear as delete + add)
- Non-existent files: Handle edge cases where files are referenced but don't exist
Acceptance Criteria
- Pre-commit hook successfully handles deleted files without errors
- Pre-commit hook successfully handles mixed changes (deletions + modifications)
- Pre-commit hook successfully handles file renames
- Pre-commit hook maintains all existing functionality for non-deleted files
- Informational messages are displayed when skipping restore operations
- No regression in existing pre-commit hook behavior
Related Files
core-web/.husky/pre-commit- The pre-commit hook script that needs updating
Priority
Medium - This is a quality-of-life improvement that prevents workflow disruption but doesn't block critical functionality. Developers can currently work around it using --no-verify, but fixing it improves the overall developer experience.
Notes
- This fix was identified during a refactoring session where multiple files were being moved/renamed
- The fix is backward compatible and doesn't change the behavior for non-deleted files
- The solution uses standard git commands and bash patterns already present in the hook
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.