Skip to content

Repository files navigation

En Linux puedes probar el programa directamente con: python3 -m repopath_sanitizer

RepoPath Sanitizer

A PyQt6 desktop app (Linux-first) that scans a directory (Git repo or any folder) for file/folder paths that would fail on Windows. It proposes safe fixes and can apply them using either git-aware renames (git mv) or pure filesystem renames (os.rename) depending on the mode.

Debian 12 Tested Python License


Features

  • Two modes: Git Repository / Any Folder (Filesystem) — toggle in the toolbar
    • Git mode: scans with git ls-files, renames tracked files with git mv, untracked with os.rename
    • Filesystem mode: scans any directory with os.walk, renames everything with os.rename — zero git dependency
  • Detects Windows-incompatible paths in directories and Git repositories
  • Reports tracked files and normal untracked files; ignored files are optional
  • Git-aware renames (git mv) to preserve history
  • Filesystem renames (os.rename) for untracked files — no git needed
  • Auto-switches between os.rename (untracked) and git mv (tracked) automatically
  • Collision detection (case-insensitive + Unicode NFC)
  • Long path and long file/folder name detection with shortening strategies
  • Estimated Windows checkout path detection using a configurable base folder
  • GUI + CLI modes
  • Safe undo system
  • Results context menu for opening paths in the file manager or copying paths
  • Reusable windows_rules.py module with all Windows path restrictions
  • Opens maximized by default with minimum size protection (960×540)
  • Window geometry persistence — remembers size/position between sessions
  • Thread-safe scanning — prevents crashes on double-click or window close

Fixed Case: Windows Clone Failure Caused by Trailing Periods

This project now documents and tests an important real-world case that breaks git clone or git checkout on Windows:

  • Problematic path example: Promts/Acerca de.../About Juan y Washington.txt

The problem was not the file About Juan y Washington.txt itself. The real issue was the directory name Acerca de..., because Windows does not allow file or folder names to end with a period (.) or a space.

On Linux, Git can store and check out that path without trouble. On Windows, the clone may download successfully but fail during checkout with an error similar to:

error: invalid path 'Promts/Acerca de.../About Juan y Washington.txt'
fatal: unable to checkout working tree

RepoPath Sanitizer already had the trailing-space/trailing-period rule, and this case is now explicitly covered in tests and documentation so it remains protected against regressions.

The automatic fix is to trim the invalid trailing periods from the affected segment:

  • Original: Promts/Acerca de.../About Juan y Washington.txt
  • Fixed: Promts/Acerca de/About Juan y Washington.txt

Screenshots

Main Window (Light Theme)

Main window light theme

Main Window (Dark Theme)

Main window dark theme


Runtime Requirements

RepoPath Sanitizer requires:

  • Python 3.10+
  • Git (used for safe git mv operations)
  • PyQt6

Install on Debian 12:

sudo apt install python3 python3-pyqt6 git

This program was tested on Debian 12 (Bookworm).


Linux File Dialog Fix

If yo use this program in a non KDE Linux, when the GUI was launched with:

QT_QPA_PLATFORMTHEME=qt5ct

Under that backend, yo cannot search for folders o files with "Ctrl + F"

In this project, the solution is already integrated in the code. RepoPath Sanitizer detects Linux GUI startup and, before creating QApplication, it checks QT_QPA_PLATFORMTHEME. If the value is empty or qt5ct, it changes it to gtk3.

Why gtk3 helped

In this environment, the GTK3-backed dialog behaved better than the Qt dialog backend:

  • file dialogs opened immediately
  • the search box worked correctly
  • bookmarks places from GTK worked correctly

How the integrated fix works

The logic used by this project is simple and can be reused in other PyQt6 applications:

import os
import sys

if sys.platform.startswith("linux"):
    current = os.environ.get("QT_QPA_PLATFORMTHEME", "")
    if current in {"", "qt5ct"}:
        os.environ["QT_QPA_PLATFORMTHEME"] = "gtk3"

Important: this must run before creating QApplication.

Manual workaround for other programs

If another PyQt6 program does not have this fix in its own code, you can launch it manually from its repository with a command such as:

QT_QPA_PLATFORMTHEME=gtk3 python3 -m repopath_sanitizer

For RepoPath Sanitizer specifically, this manual command is only a fallback or test command because the fix is already built in.

This affects GUI dialogs such as:

  • Browse...
  • Save Log
  • report export dialogs

Run directly on Debian, Ubuntu

On Debian you can use the system PyQt6 package (APT). if your dependencies are already installed from the Debian repositories, you can test the program directly without creating a venv:

python3 -m repopath_sanitizer

Run under venv

Using pip and a virtual environment if you need

sudo apt update
sudo apt install python3 python3-venv python3-pyqt6 git

python3 -m venv .venv --system-site-packages
source .venv/bin/activate

pip install -U pip
pip install -e .[dev] --no-deps

repopath-sanitizer

Explanation

--system-site-packages allows the virtual environment to use PyQt6 installed via APT.
--no-deps prevents pip from trying to reinstall PyQt6 from PyPI.

The second time you want to launch the program, just put:

source .venv/bin/activate
repopath-sanitizer

CLI Mode

repopath-sanitizer --cli --repo /path/to/repo --json out.json --text out.txt

If you want the scan to estimate the real Windows clone destination more accurately, set the expected base folder:

repopath-sanitizer --cli --repo /path/to/repo --checkout-root "C:\Users\Juan\Documents\Projects"

Safety Notice

This tool performs Git renames (git mv).
Always review changes with:

git status
git diff

before committing.

Shared Git Hook

This repository also includes a shared pre-commit hook in .githooks/pre-commit.

If you want to use the same protection in all your repositories on Linux, use the global hook package in global-hooks/README.md.

It blocks commits when staged paths contain Windows-incompatible problems such as:

  • forbidden characters
  • reserved Windows device names
  • trailing spaces or trailing periods
  • file or folder names that exceed the configured segment limit
  • repository-relative paths that exceed the configured path limit
  • estimated Windows checkout paths that become too long after adding the clone base folder

To enable it in this repository:

git config core.hooksPath .githooks
chmod +x .githooks/pre-commit

To enable it globally for all your repositories, see:

Optional environment variables for stricter or more realistic checks:

export REPOPATH_SANITIZER_MAX_PATH=260
export REPOPATH_SANITIZER_MAX_SEGMENT=255
export REPOPATH_SANITIZER_CHECKOUT_ROOT='C:\Users\Juan\Documents\Projects'

How It Works

Mode Selection

At the top of the window, a Mode dropdown lets you choose:

Mode Description
Git Repository Uses git ls-files to find files. Renames tracked files with git mv, untracked files with os.rename. Shows stash warnings for tracked renames.
Any Folder (Filesystem) Uses os.walk to find files. No git required at all. Renames everything with os.rename. Works on regular project folders that are not git repos.

The scanner (Git mode):

  1. Uses git ls-files to enumerate tracked files and normal untracked files
  2. Validates each path against Windows filesystem rules
  3. Detects:
    • forbidden characters
    • reserved device names
    • trailing spaces/periods
    • total path length issues
    • individual file/folder name length issues
    • estimated final Windows checkout path length issues
    • case-insensitive collisions
    • Unicode normalization conflicts
  4. Proposes safe sanitized paths
  5. Applies fixes using the appropriate strategy:
    • git mv for tracked files (preserves git history)
    • os.rename for untracked files (no git dependency)

The program auto-switches between these two strategies. When you click Apply Fixes, it shows a single combined preview with each file labeled [untracked] or [git mv], then applies them all in one operation.

The scanner (Filesystem mode):

  1. Uses os.walk to enumerate all files and directories
  2. Skips hidden directories (.git, .svn, etc.) and hidden files
  3. Validates each path against Windows filesystem rules
  4. Detects the same issues as Git mode
  5. Proposes safe sanitized paths
  6. Applies fixes with os.rename() — no git commands, no stash

Why two strategies?

If you accidentally type a Windows-forbidden character (like : or |) in a filename on Linux and haven't done git add yet, the file is untracked. Using git mv on an untracked file would fail. Instead, the program uses os.rename() directly — no stash, no git dependency. After renaming, you can git add the corrected names.

When does the stash warning appear?

The "uncommitted changes" warning only appears when there are tracked files that need renaming (Git mode only). If the only issues are with untracked files, the program renames them directly without asking about stash. In Filesystem mode, the stash warning never appears.

Using Filesystem mode for non-git folders

If you have a regular project folder (not a git repo) and want to check it for Windows compatibility:

  1. Switch the Mode dropdown to Any Folder (Filesystem)
  2. Click Browse... and select your folder
  3. Click Scan
  4. Review the issues and click Apply Fixes

All renames are done with os.rename() — no git commands are executed. This is safe for any directory.

For the Windows checkout failure described above, the relevant rule is trailing spaces/periods. If a path segment ends in . or space, the sanitizer flags it and proposes a trimmed replacement that Windows can store safely.

The scanner also detects repositories that may fail on Windows because the final checkout path becomes too long after combining:

  • the Windows base folder
  • the repository folder name
  • deep nesting of folders and subfolders
  • long file or folder names

This matters because a repository may look acceptable on Linux while still failing on Windows when cloned under a path such as C:\Users\Name\Documents\Projects\....

In the GUI, these length-related issues are shown separately so they are easier to understand:

  • Relative path too long
  • File/folder name too long
  • Estimated Windows checkout path too long

Reusable windows_rules.py Module

The file src/repopath_sanitizer/windows_rules.py is a standalone, importable module that contains all Windows filesystem path restrictions. Any developer can use it in their own project without depending on the rest of RepoPath Sanitizer.

What it contains

Symbol Description
FORBIDDEN_CHARS Characters banned in Windows filenames: `< > : " / \
RESERVED_DEVICE_NAMES Device names Windows treats as special: CON, PRN, AUX, NUL, COM1–COM9, LPT1–LPT9
MAX_PATH_LENGTH Maximum total path length: 260
MAX_SEGMENT_LENGTH Maximum single segment length: 255
is_valid_filename(seg) Returns True if the segment is valid on Windows
sanitize_segment(seg) Returns a Windows-safe version of the segment
sanitize_relative_path(path) Sanitizes every segment in a path
validate_relative_path(path) Returns a list of (code, message) issues
contains_forbidden(seg) Checks for forbidden characters
has_trailing_space_or_period(seg) Checks for trailing . or
is_reserved_device_name(seg) Checks for reserved device names

Usage in other projects

from repopath_sanitizer.windows_rules import (
    is_valid_filename,
    sanitize_segment,
    validate_relative_path,
    FORBIDDEN_CHARS,
)

if not is_valid_filename("my:file.txt"):
    safe = sanitize_segment("my:file.txt")
    print(f"Renamed to: {safe}")  # → my -file.txt

issues = validate_relative_path("src/my:file.txt")
for code, msg in issues:
    print(f"{code}: {msg}")

GUI Improvements

Maximized by default

The application opens maximized on all screen sizes. A minimum size of 960×540 is enforced to prevent the UI from becoming unusable on small displays.

Window geometry persistence

The window's size, position, and maximized state are saved between sessions. When you reopen the app, it restores the exact same layout.

Thread safety

Scanning runs in a background thread. If you click Scan twice quickly, the previous scan is cancelled cleanly before the new one starts. Closing the window while a scan is running is also safe — threads are terminated before the application exits.


Developer Requirements

For development and testing:

sudo apt install python3-pytest

Project Structure (for developers)

src/repopath_sanitizer/
    ui_main.py        # GUI
    engine.py         # Scan logic
    pathrules.py      # Windows compatibility rules
    windows_rules.py  # Reusable Windows path restrictions (importable standalone)
    gitutils.py       # Git operations
    worker.py         # Background tasks
    report.py         # JSON/Text reports
    state.py          # Undo system
    cli.py            # CLI mode

Debian Packaging Dependencies

To build the .deb package you need:

sudo apt install debhelper dh-python python3-all pybuild-plugin-pyproject \
    python3-pyqt6 python3-pytest git

Then build with:

sudo apt build-dep .
dpkg-buildpackage -us -uc

Translations (Qt Linguist / Qt Creator)

The application is prepared for internationalization.

Install tools:

sudo apt install qtcreator qttools5-dev-tools qt6-tools-dev-tools

Workflow to add a language:

pylupdate6 src -ts translations/repopath_sanitizer_es.ts
linguist translations/repopath_sanitizer_es.ts
lrelease translations/repopath_sanitizer_es.ts

Translation files (.qm) are installed to:

/usr/share/repopath-sanitizer/translations/

About

A PyQt6 desktop app (Linux-first) that scans a local Git working tree and finds file/folder paths that would fail to check out on Windows. It proposes safe fixes and can apply them using git-aware renames (git mv) to preserve history.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages