- Python 100%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .github/workflows | ||
| gdrive_dedupe | ||
| scripts | ||
| tests | ||
| .gitignore | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| requirements.txt | ||
gdrive-dedupe
gdrive-dedupe is a metadata-only Google Drive deduplication tool for very large drives.
It detects both duplicate files and duplicate folder trees without downloading file contents.
Features
- Streams Google Drive metadata with paginated
files.listcalls - Stores normalized metadata in indexed SQLite tables
- Detects duplicate files by MD5 checksum
- Detects duplicate folders via deterministic bottom-up tree hashing
- Supports resumable scans
- Produces CLI tables and HTML reports with Google Drive links for manual review
- Highlights actionable top-level duplicate root sets with keep-vs-review candidates
- Visualizes largest folder trees, highest file-count folders, and
.githotspots
Architecture
Designed for large datasets (500k+ files, multi-TB drives):
- Streaming ingestion into SQLite (no full dataset in memory)
- Batched upserts and indexed queries
- Bottom-up folder hashing with queue-based processing
- Persistent scan state for resume support
Installation
Choose one workflow:
Option A: pip + venv
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip install .
Option B: uv (faster dependency management)
Install uv: https://docs.astral.sh/uv/
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt
uv pip install -e .
Google API and OAuth setup
Follow the official Google Drive quickstart flow for desktop OAuth apps:
1) Create or select a Google Cloud project
- Open Google Cloud Console
- Select an existing project or create a new one
2) Enable the Google Drive API
- Open the API page directly: Google Drive API in API Library
- Click
Enable(orManageif already enabled)
3) Configure OAuth consent
- Open Google Auth Platform
- Configure app details (name, support email, developer contact)
- Choose audience:
Internalfor Google Workspace org-only usageExternalfor personal Gmail/multi-user usage
- If using
Externalin testing mode, add your account under test users
4) Create Desktop OAuth client credentials
- Open Credentials page
- Click
Create credentials->OAuth client ID - Application type:
Desktop app - Download the JSON file
- Save it as
credentials.jsonin your project root (or any path you prefer)
5) Run the first scan and complete OAuth login
gdrive-dedupe scan --credentials /path/to/credentials.json
On first run, the tool opens a browser for consent. After approval, the local token is stored at:
~/.config/gdrive-dedupe/token.json
If you change scopes or OAuth client, delete that token file and authenticate again.
Usage
gdrive-dedupe scan
gdrive-dedupe duplicates files
gdrive-dedupe duplicates folders
gdrive-dedupe duplicates waste --limit 20 --offset 0
gdrive-dedupe report --output report.html
gdrive-dedupe stats
Optional shared-drive scan:
gdrive-dedupe scan --drive-id <DRIVE_ID>
Largest duplicate waste first (then next page):
gdrive-dedupe duplicates waste --limit 20 --offset 0
gdrive-dedupe duplicates waste --limit 20 --offset 20
gdrive-dedupe duplicates waste --limit 20 --offset 40
Filter out small savings:
gdrive-dedupe duplicates waste --min-reclaimable 2GB --limit 20
Interactive cleanup review (largest duplicate roots first), opening up to 5 Drive folders at once:
gdrive-dedupe duplicates waste --interactive --open-links 5 --open-mode windows
Generate an HTML report with all candidate links for actionable duplicate roots:
gdrive-dedupe report --output report.html --actionable-candidates 0
CLI options reference
Full command tree:
gdrive-dedupe [OPTIONS] COMMAND [ARGS]...
Top-level options:
--install-completionInstall completion for the current shell.--show-completionPrint completion script for the current shell.--helpShow help.
Top-level commands:
scanreportstatsduplicates(files,folders,waste)
scan
gdrive-dedupe scan [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--credentials PATHOAuth client JSON path. Default:credentials.json--query TEXTDrivefiles.listfilter. Default:trashed = false--drive-id TEXTOptional shared drive ID.--resume / --no-resumeResume from stored scan page token. Default:--resume--page-size INTEGERRange100..1000. Default:1000--help
report
gdrive-dedupe report [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--output PATHOutput HTML path. Default:report.html--limit INTEGERMax duplicate groups per section (>=1). Default:100--actionable-candidates INTEGERDelete-candidate links per actionable root (>=0,0 = all). Default:25--recompute / --no-recomputeRecompute folder hashes before report. Default:--recompute--workers INTEGERHash worker threads (1..16). Default:1--help
stats
gdrive-dedupe stats [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--help
duplicates
gdrive-dedupe duplicates [OPTIONS] COMMAND [ARGS]...
--help
Subcommands:
filesfolderswaste
duplicates files
gdrive-dedupe duplicates files [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--limit INTEGERMax duplicate groups to display. Default:50--help
duplicates folders
gdrive-dedupe duplicates folders [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--recompute / --no-recomputeRecompute folder hashes before listing. Default:--recompute--workers INTEGERHash worker threads (1..16). Default:1--limit INTEGERMax duplicate groups to display. Default:50--help
duplicates waste
gdrive-dedupe duplicates waste [OPTIONS]
--db PATHSQLite metadata DB path. Default:~/.config/gdrive-dedupe/metadata.db--recompute / --no-recomputeRecompute folder hashes before ranking reclaimable roots. Default:--no-recompute--workers INTEGERHash worker threads (1..16). Default:1--limit INTEGERRows to show (>=1). Default:15--offset INTEGERRow offset for pagination (>=0). Default:0--min-reclaimable TEXTThreshold like500MB,2GB,0. Default:1MB--sample-candidates INTEGERCandidate folders per row (1..10). Default:3--interactive / --no-interactiveStart interactive ranking review loop. Default:--no-interactive--open-links INTEGERLinks opened per interactive action (1..20). Default:5--open-mode [tabs|windows]Browser open target style. Default:windows--help
Review workflow (no CLI deletion)
gdrive-dedupe is intentionally non-destructive. It does not delete files/folders from the command line.
Recommended workflow:
- Run
gdrive-dedupe report --output report.html - Open the generated report in your browser
- Start with
Actionable Duplicate Roots(top-level duplicate sets) to remove noise- Nested duplicate descendants are suppressed so you only review root duplicate folders
- Review
Folder Size HotspotsandFile Count Hotspotsfor high-impact cleanup - Check
Repository Metadata Hotspots (.git)for repo artifacts to remove - Use
Open file in Drive/Open folder in Drivelinks to inspect duplicates - Decide and delete manually in Google Drive UI
Interactive shortcuts (gdrive-dedupe duplicates waste --interactive):
o: open keep folder + top delete candidates in browserd: open only delete-candidate folders in browserk: open only keep folder in browsern: next ranked candidatep: previous ranked candidatej <rank>: jump to a rank numberq: quit interactive mode
Database schema
files(id, name, parent, size, md5, mimeType)
folders(id, parent, name)
folder_hash(folder_id, hash)
Indexes:
files(md5)files(parent)folders(parent)folder_hash(hash)
Duplicate detection logic
Duplicate files
SELECT md5, COUNT(*)
FROM files
WHERE md5 IS NOT NULL
GROUP BY md5
HAVING COUNT(*) > 1;
Duplicate folders
For each folder:
- Leaf hash:
SHA256(sorted(file_md5_list)) - Non-leaf hash:
SHA256(sorted(file_md5_list + child_folder_hashes))
Folders with identical hash values are duplicates of the same tree shape/content.
Performance notes
- Uses
pageSize=1000for Drive API pagination - Avoids loading all files/folders into RAM
- Uses WAL-mode SQLite for better write/read concurrency
- Supports thread workers for folder hash computation
- Real scan example from a user drive:
Scanning metadata: 489742items [23:48, 342.79items/s]Scan complete. Items scanned: 489742
Security considerations
- Uses read-only metadata scope:
drive.metadata.readonly - Never downloads file content
- Stores OAuth token locally under
~/.config/gdrive-dedupe/ - Do not commit credentials or token files
Development
pip workflow:
ruff check .
black --check .
pytest
uv workflow:
uv run ruff check .
uv run black --check .
uv run pytest
CI runs lint, format checks, tests, and install validation.
License
MIT