Skip to main content

A safe editing wrapper: edits a temp copy, compares, and saves original backup if changed.

Project description

License Language GitHub Release PyPI - Version PyPI downloads

mirro

mirro is a tiny safety-first editing wrapper for text files. You edit a temporary file, mirro detects whether anything changed, and if it did, it saves a backup of the original before writing your changes.

Why mirro?

Well... have you ever been in the “ugh, I forgot to back this up first” situation?

No?

Stop lying... 🥸

mirro gives you a built-in safety net:

  • never edits the real file directly

  • detects whether the edit actually changed content

  • creates a timestamped backup only when changes occurred

  • clearly labels backups so you know exactly what they came from

  • respects the user’s $EDITOR when possible

  • requires sudo only when actually needed

  • accepts most of your favourite editor's flags

It’s simple, predictable, and hard to misuse.

I mean... the only thing you need to remember is to use it.

How it works

mirro reads the original file (or pre-populates new files with a friendly message).

It writes that content into a temporary file.

It launches your $EDITOR to edit the temp file.

When the editor closes, mirro compares old vs new.

If nothing changed:

file hasn't changed

If changed:

file changed; original backed up at: ~/.local/share/mirro/ (or /root/.local/share/mirro/ under sudo)

Backed up files include a header:

# ---------------------------------------------
# mirro backup
# Original file: /path/to/whatever.conf
# Timestamp: 2025-11-10 17:44:00 UTC
# ---------------------------------------------

So you never lose track of the original location.

Backup directory

By default all the backups will be stored at:

~/.local/share/mirro/

so under sudo:

/root/.local/share/mirro/

Backups are named like:

filename.ext.orig.20251110T174400

Functionalities

List all backup files stored in your backup directory.

mirro --list

Output includes permissions, owner/group, timestamps, and backup filenames.

Restore the most recent backup for a given file.

mirro --restore-last ~/.config/myapp/config.ini

This:

  1. finds the newest backup matching the filename,

  2. strips the mirro header from it,

  3. and overwrites the target file with its original contents.

Remove old backup files.

mirro --prune-backups

This removes backups older than the number of days set in MIRRO_BACKUPS_LIFE.

Remove backups older than N days

mirro --prune-backups=14

This keeps the last 14 days of backups and removes everything older.

Remove all backups

mirro --prune-backups=all

This deletes every backup in the backup directory.

Environment Variable

MIRRO_BACKUPS_LIFE controls the default number of days to keep when using mirro --prune-backups. Its default value is 30 if not set otherwise.

export MIRRO_BACKUPS_LIFE=7

Backups older than 7 days will be removed.

Invalid or non-numeric values fall back to 30 days.

Note: a value of 0 is invalid.

Built-in diff

This shows a git-like diff of the current file version and any of that file backups.

mirro --diff file file.orig.20251121T163121

Shows current directory's history

Shows which files in the current directory have edit history recorded by mirro. For each file, it prints how many revisions exist and when the latest one was saved.

mirro --status

Files with history in /foo/bar:
  baz.conf         (3 revisions, latest: 2025-01-12 14:03 UTC)

Installation

From package manager

This is the preferred method of installation.

Ubuntu 22.04 and 24.04

sudo add-apt-repository ppa:mdaleo/mirro
sudo apt update
sudo apt install mirro

Fedora 41, 42, 43

sudo dnf copr enable mdaleo/mirro
sudo dnf install mirro

From PyPI

NOTE: To use mirro with sudo, the path to mirro must be in the $PATH seen by root.
Either:

  • install mirro as root, or
  • add the path to mirro to the secure_path parameter in /etc/sudoers. For example, where /home/user/.local/bin is where mirro is:
Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/home/user/.local/bin"

Install with:

pip install mirro

From this repository

git clone https://github.com/guardutils/mirro.git
cd mirro/
poetry install

TAB completion

Add this to your .bashrc

eval "$(register-python-argcomplete mirro)"

And then

source ~/.bashrc

How to run the tests

  • Clone this repository

  • Ensure you have Poetry installed

  • Run poetry run pytest -vvvv --cov=mirro --cov-report=term-missing --disable-warnings

pre-commit

This project uses pre-commit to run automatic formatting and security checks before each commit (Black, Bandit, and various safety checks).

To enable it:

poetry install
poetry run pre-commit install

This ensures consistent formatting, catches common issues early, and keeps the codebase clean.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mirro-0.5.0.tar.gz (19.4 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mirro-0.5.0-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file mirro-0.5.0.tar.gz.

File metadata

  • Download URL: mirro-0.5.0.tar.gz
  • Upload date:
  • Size: 19.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.13.9 Linux/6.17.9-200.fc42.x86_64

File hashes

Hashes for mirro-0.5.0.tar.gz
Algorithm Hash digest
SHA256 50dc7af4fde4b77ae0a9346b4f00bf19fd882a3b23f8afd90a2a28bf092bc6b4
MD5 cadbf5c804f3882dac05e69ce93a681d
BLAKE2b-256 a25b43c749cf9b45e2abc2ffca7dcb015b1f79e3164f026f4ced1f2a18e3c02e

See more details on using hashes here.

File details

Details for the file mirro-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: mirro-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 20.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: poetry/1.8.4 CPython/3.13.9 Linux/6.17.9-200.fc42.x86_64

File hashes

Hashes for mirro-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 62547f420ae5cad8e79de23598e8ad6888fd64fe0aee786d38c77f07a9a91cf0
MD5 1786d7a2b719804058e54588d2847121
BLAKE2b-256 61224a8e8115cc73862b1279c56d19468bce5914bc1f8d4a7f781ea841584f0c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page