Git repository synchronization tool
Project description
twshtd
Git repository synchronization tool for managing multiple repositories.
Installation
# Install with uv
uv tool install twshtd
# Or install from source
uv tool install -e .
Usage
twshtd [OPTIONS] COMMAND [ARGS]
Commands
| Command | Description |
|---|---|
push |
Commit and push all configured repositories |
pull |
Commit local changes and pull from remotes |
info |
Show configuration and repository status |
edit |
Open configuration file in $EDITOR |
dirty |
Show dirty/behind/ahead status for repos in directories |
Options
| Option | Description |
|---|---|
-v, --verbose |
Enable debug logging |
-V, --version |
Show version |
Push Command
twshtd push [OPTIONS]
Commits all changes and pushes to remote for each configured repository.
| Option | Description |
|---|---|
-c, --config |
Config file path |
-w, --workers |
Parallel workers (number or 'auto') |
-n, --dry-run |
Show what would be done |
Pull Command
twshtd pull [OPTIONS]
Commits local changes and pulls from remotes. Supports two modes per repository:
pull: Full git pull (default)fetch: Only fetch, showing what changed
| Option | Description |
|---|---|
-c, --config |
Config file path |
-w, --workers |
Parallel workers (number or 'auto') |
-n, --dry-run |
Show what would be done |
Dirty Command
twshtd dirty [OPTIONS]
Scans configured directories for git repositories and shows their status.
| Option | Description |
|---|---|
-c, --config |
Config file path |
--no-fetch |
Skip git fetch before checking status |
Configuration
Config file location (in order of precedence):
TWSHTD_CONFIGenvironment variable~/.config/twshtd/repos.toml
Example Configuration
[settings]
workers = 1 # parallel workers (1 = sequential)
# Global hooks - run before/after ALL repos
pre_commands = [
"! pgrep -x anki", # Block push if Anki is running
]
post_commands = []
[[repos]]
path = "~/dev/project1"
pull_mode = "pull" # "pull" or "fetch"
pre_commands = ["make clean"] # run before push (all must pass)
post_commands = ["make build"] # run after pull (warn on failure)
enabled = true
[[repos]]
path = "$PROJECTS/project2"
pull_mode = "fetch"
pre_commands = ["git stash list"] # multiple commands supported
post_commands = ["make test", "make lint"]
[[repos]]
path = "~/dev/disabled-repo"
enabled = false
# Directories to scan with 'dirty' command
[[dirty]]
path = "~/dev"
enabled = true
[[dirty]]
path = "$WORK/repos"
Path Resolution
Paths support:
- Home directory expansion (
~) - Environment variables (
$VARor${VAR})
Features
Hooks (Pre-Commands / Post-Commands)
Hooks allow you to run shell commands at specific points in the workflow.
Global Hooks
Define in [settings] to run for ALL repos:
[settings]
pre_commands = ["! pgrep -x anki"] # Block if Anki running
post_commands = ["echo 'All done'"]
- pre_commands: Run once before processing any repos. ALL must succeed (exit 0) or the entire operation stops.
- post_commands: Run once after all repos are processed. Failures show warnings but don't fail the operation.
Per-Repo Hooks
Define per repository for repo-specific actions:
[[repos]]
path = "~/dev/myproject"
pre_commands = ["make clean", "npm ci"] # before push
post_commands = ["make build", "make test"] # after pull
- pre_commands: Run before push. ALL must succeed or the repo is skipped (other repos continue).
- post_commands: Run after pull. Failures show warnings but the repo is marked as successful.
Commands run in the repository directory (per-repo) or current directory (global).
Execution Order
PUSH:
1. Global pre_commands (all must pass, or exit 1)
2. For each repo:
a. Repo pre_commands (all must pass, or skip this repo)
b. Git commit + push
3. Global post_commands (warn on failure)
PULL:
1. Global pre_commands (all must pass, or exit 1)
2. For each repo:
a. Git pull/fetch
b. Repo post_commands (warn on failure)
3. Global post_commands (warn on failure)
Commit Message
When committing changes, twshtd uses git status --porcelain output as the commit message,
providing a clear record of what changed.
Example: Blocking Push if Anki Running
To prevent pushing when Anki is running (e.g., to avoid sync conflicts with Anki repos), use a global pre_command:
[settings]
pre_commands = ["! pgrep -x anki"]
This command exits 1 (failure) if Anki is running, blocking the push.
rplc Integration
For repositories using rplc, twshtd automatically swaps out
files before committing. Use --skip-rplc to bypass.
Development
# Install dev dependencies
uv sync
# Run tests
make test
# Lint and format
make static-analysis
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file twshtd-1.1.1.tar.gz.
File metadata
- Download URL: twshtd-1.1.1.tar.gz
- Upload date:
- Size: 14.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ccdf632bc2cf126e127c4c88eecdea346ee679bd605e315f02419cf25d27d85f
|
|
| MD5 |
32744b7f138c1c82c73400a0cf2ab520
|
|
| BLAKE2b-256 |
bfcf3ddf7d663ba60d99a5057479e82f81074323bea70dd54196298c053df268
|
File details
Details for the file twshtd-1.1.1-py3-none-any.whl.
File metadata
- Download URL: twshtd-1.1.1-py3-none-any.whl
- Upload date:
- Size: 13.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1118814afbac8ed0f2187277502248f92289a833b1e9d99f186bc386eb8bf28b
|
|
| MD5 |
86e83bd96ed89a7250158e28fad4d26a
|
|
| BLAKE2b-256 |
d60e2f3cff307b5efa7d48ee1c003022404ab2f96335db123876bc2bc7b62356
|