dotCMS / dotCMS/core

Pre-Commit Hook: Handle Deleted Files Gracefully

Open
#33,974 0 comments 0 reactions 1 assignee View on GitHub

@hmoreras is already working on this.

Since Dec 2, 2025.

Team : Modernization
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:

  1. Line ~292: When restoring unmatched files that were modified by linting/formatting
  2. Line ~531: When backing up untracked files during the restore process
  3. 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-verify to 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:

  1. Check for deleted files first: Before calling git restore, check if the file is marked as deleted (D) in staged changes
  2. Skip restore for deleted files: If a file is deleted, skip the restore operation and log an informational message
  3. 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

  1. Robust Error Handling: The hook no longer fails when files are deleted
  2. Better Developer Experience: Developers can commit refactoring changes without bypassing hooks
  3. Clear Feedback: Informational messages explain why certain operations are skipped
  4. Maintains Functionality: All existing functionality remains intact for non-deleted files

Testing Scenarios

To verify the fix works correctly, test with:

  1. Deleted file in staged changes: Stage a file deletion and attempt to commit
  2. Mixed changes: Stage both file deletions and modifications together
  3. Renamed files: Stage file renames (which appear as delete + add)
  4. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.