Files
2026-10-11 13:26:08 -07:00

135 lines
4.6 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working
with code in this repository.
## Project Overview
cura-te-ipsum is a space-efficient incremental backup utility for Linux and
macOS. It creates hardlink-based snapshots similar to Time Machine/rsnapshot,
where unchanged files share inodes between backups to minimize storage.
## Development Commands
### Environment Setup
Project uses `uv` for Python version and dependency management:
```bash
uv sync --all-extras --dev
```
### Testing
```bash
uv run pytest # run all tests
uv run pytest -v # verbose output
uv run pytest tests/test_fs.py # run specific test file
```
### Installation
```bash
uv pip install . # install from source
```
### Running the Tool
```bash
cura-te-ipsum -b /path/to/backups /path/to/source
cura-te-ipsum -b /backups /src1 /src2 --dry-run --verbose
```
## Architecture
### Core Modules
**curateipsum/backup.py**: Main backup orchestration
- `initiate_backup()`: Main entry point - coordinates hardlinking from
previous backup and rsync from sources
- `cleanup_old_backups()`: Implements grandfather-father-son retention
(daily/weekly/monthly/yearly) with configurable thresholds
- Lock management (`set_backups_lock`, `release_backups_lock`): Prevents
concurrent backups using PID-based lock file
- Backup markers (`set_backup_marker`, `_is_backup`): Tracks completed vs
incomplete backups
**curateipsum/fs.py**: Low-level filesystem operations
- `rsync()`: Pure Python rsync implementation - syncs source to destination
with delete support
- `rsync_ext()`: Wrapper for external rsync command
- `hardlink_dir()`: Recursively hardlinks entire directory tree (Python or
external cp)
- `nest_hardlink()`: Hardlinks individual files to `.backup_delta` directory
preserving structure
- `scantree()`: Recursive directory iterator
- `copy_direntry()`, `update_direntry()`, `rm_direntry()`: DirEntry
manipulation utilities
**curateipsum/cli.py**: Command-line interface
- Argument parsing and validation
- Platform check (Linux/macOS only)
- Logging setup and execution flow
### Key Data Structures
**PseudoDirEntry**: Emulates os.DirEntry for paths that don't exist yet or
for consistency in handling both real and pseudo entries. Used extensively
in backup operations.
**Actions enum**: Tracks filesystem changes during rsync (NOTHING, DELETE,
REWRITE, UPDATE_TIME, UPDATE_PERM, UPDATE_OWNER, CREATE, ERROR).
### Backup Process Flow
1. Acquire lock file (`.backups_lock`) with PID
2. Clean up old backups per retention policy
3. Hardlink entire previous backup directory to new timestamped directory
4. Remove backup markers from hardlinked copy
5. Delete `.backup_delta` directory from hardlinked copy
6. Rsync each source to new backup (overwrites changed files, breaking
hardlinks)
7. For each changed file, hardlink to `.backup_delta` directory
8. Set backup marker (`.backup_finished_TIMESTAMP`)
9. Release lock
### Important Implementation Details
**Retention policy**: `cleanup_old_backups()` implements threshold-based
retention where each threshold is inclusive. For example, `keep_weekly=1`
on Thursday keeps one backup from current week AND one from previous week.
**Delta tracking**: Changed files are hardlinked to `.backup_delta/` within
each backup to track what changed in that specific backup snapshot.
**Hardlink breakage**: When rsync overwrites a hardlinked file, it breaks
the hardlink, creating a new inode. This is how changed files get tracked
while unchanged files continue sharing inodes.
**External tools**: Can use external rsync (--external-rsync) or GNU cp/gcp
for hardlinking (--external-hardlink) instead of Python implementations.
macOS requires `gcp` (GNU cp) for external hardlinking.
**Dry run behavior**: Creates backup directory and performs operations but
removes directory at end without setting backup marker.
## Platform Specifics
- Linux: Uses standard `cp` command
- macOS: Requires `gcp` (GNU coreutils) for external hardlinking
- Only these platforms are supported (checked in cli.py)
## Testing
Tests use unittest.TestCase with mock.patch for datetime manipulation.
Focus is on backup cleanup logic with various retention scenarios.
Tests create temporary backup directories and verify correct backups are
kept/removed.
## Version Management
Version is automatically derived from git tags using `setuptools_scm`.
The version file `curateipsum/_version.py` is auto-generated during build
and should not be committed to version control.
To create a new release, tag the commit:
```bash
git tag -a v0.0.4 -m "Release 0.0.4"
git push origin v0.0.4
```