Metadata-Version: 2.4
Name: repofleet
Version: 0.2.0
Summary: Clone, update and keep a fleet of git repositories in sync from a single declarative config.
Author-email: Srivathsa Kavirayuni <kavirayuni.dev@gmail.com>
License-Expression: MIT
Project-URL: Homepage, https://github.com/kavirayuni-dev/repofleet
Project-URL: Repository, https://github.com/kavirayuni-dev/repofleet
Project-URL: Issues, https://github.com/kavirayuni-dev/repofleet/issues
Project-URL: Changelog, https://github.com/kavirayuni-dev/repofleet/blob/main/CHANGELOG.md
Project-URL: Documentation, https://github.com/kavirayuni-dev/repofleet/blob/main/docs/USAGE.md
Keywords: git,multi-repo,monorepo,cli,developer-tools
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Version Control :: Git
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: tomli>=2.0; python_version < "3.11"
Provides-Extra: dev
Requires-Dist: pytest>=7.4; extra == "dev"
Requires-Dist: build>=1.0; extra == "dev"
Requires-Dist: twine>=5.0; extra == "dev"
Dynamic: license-file

# repofleet

**Clone, update and track a fleet of git repositories from a single declarative config.**

One command (`repofleet sync`) gets any machine into the same state:

- repos in the list but **not on disk** are cloned,
- repos already on disk are **fetched, switched to their default branch and pulled** (local work is
  auto-stashed and restored),
- repos found on disk that are **not in the list yet** are updated and then **added to the config**,
  so the shared list grows organically.

It is generic: nothing is hard-coded to a team, org or git host. Any team can ship its own
`repofleet.toml` (or a plain `repos.txt`) and get the same workflow.

**Documentation:** [full usage guide](docs/USAGE.md) · [publishing guide](docs/PUBLISHING.md) ·
[changelog](CHANGELOG.md)

## Contents

[Install](#install) · [Quick start](#quick-start) · [Commands](#commands) ·
[Configuration](#configuration) · [Output & exit codes](#output--exit-codes) ·
[Authentication](#authentication) · [Troubleshooting](#troubleshooting) ·
[Development](#development)

---

## Install

```bash
pip install repofleet          # from your package index
pipx install repofleet         # isolated, always on PATH
pip install -e ".[dev]"        # from a checkout of this folder
```

Private feed:

```bash
pip install repofleet --index-url https://pkgs.dev.azure.com/<org>/_packaging/<feed>/pypi/simple/
```

No install? Unzip the folder and use the bundled launcher:

```bash
python bootstrap.py sync
```

Requires **Python 3.9+** and **git** on `PATH`. Verify with `repofleet --version`.

---

## Quick start

### You already have the repos checked out

```bash
cd "/path/to/your/workspace"
repofleet init --name "Backend Services" --match "svc.*"
repofleet sync
```

`init` scans the folder, records every git repo it finds and writes `repofleet.toml` plus an empty
`repo-urls.txt` you can paste more URLs into.

### You have nothing yet (typical for a new joiner)

Drop the shared `repofleet.toml` in an empty folder and run:

```bash
repofleet sync
```

Everything is cloned into the workspace directory declared in the config
(`directory = "..."`).

An example profile ships with the package at `src/repofleet/profiles/example-workspace.toml` -
copy it, swap in your own URLs, and run:

```bash
repofleet sync -c example-workspace.toml
```

### You just want a list of URLs cloned somewhere

```bash
repofleet clone --repos-file repo-urls.txt --root ./workspace
```

---

## Commands

| Command | What it does |
| --- | --- |
| `repofleet init` | Create a config from the repositories found on disk. |
| `repofleet list` | Show every tracked repo and whether it is present locally. |
| `repofleet status` | Per-repo branch + clean/dirty state, and what is missing. |
| `repofleet clone` | Clone only the repositories that are missing. |
| `repofleet update` | Fetch + pull every cloned repo (add `--clone-missing` to do both). |
| `repofleet sync` | Clone missing, update existing, adopt new local repos into the config. |
| `repofleet add <url...>` | Track new repositories (`--clone` to fetch them immediately). |
| `repofleet remove <name...>` | Stop tracking repositories (files on disk are left alone). |

Common options (available on every command):

```
-c, --config FILE     config file to use (default: nearest repofleet.toml, then $REPOFLEET_CONFIG)
    --root DIR        where the repositories live / should be cloned
    --repos-file FILE extra repo list, TOML or plain text (repeatable)
    --repo URL        extra repository straight from the command line (repeatable)
    --only PATTERN    restrict to matching repo names (glob)
    --exclude PATTERN skip matching repo names (glob)
    --remote NAME     remote to use (default: origin)
-j, --jobs N          run N repos in parallel
    --dry-run         print the plan without touching anything
-q, --quiet           summary only
```

`update` / `sync` also accept `--no-stash` (skip dirty repos instead of stashing) and `--no-prune`.
`sync` accepts `--no-adopt` to disable writing newly discovered repos back to the config.

Examples:

```bash
repofleet update --only "svc.a*" -j 8
repofleet clone --repos-file repo-urls.txt --root ./workspace
repofleet sync --dry-run
repofleet add https://dev.azure.com/org/Project/_git/new.service --clone
```

Every flag is documented in the [usage guide](docs/USAGE.md#5-command-reference).

---

## Configuration

### `repofleet.toml`

```toml
[workspace]
name      = "Backend Services"
# "auto": use this file's folder when it already holds repos, else use `directory`.
root      = "auto"
directory = "Backend Services"
match     = ["svc.*"]          # which local folders may be auto-adopted
remote    = "origin"
repos_file = "repo-urls.txt"   # optional: extra repos listed one URL per line

[defaults]
stash     = true   # stash local changes before pulling, restore afterwards
prune     = true   # git fetch --prune
jobs      = 4      # parallel workers
autoadopt = true   # write newly discovered local repos back into this file

[[repos]]
name = "svc.api"
url  = "https://github.com/org/svc.api.git"
# branch = "develop"   # optional, defaults to the remote's default branch
```

`name` is optional - it is derived from the URL when omitted.

### Plain text list (easiest to share)

```
# repo-urls.txt
https://github.com/org/svc.api.git
https://github.com/org/tooling.git
custom-folder-name = https://github.com/org/other.git
https://github.com/org/legacy.git   release/2024   # pin a branch
```

Use it with `--repos-file repo-urls.txt`, or reference it from `repos_file` in the TOML.
`repofleet init` creates this file for you, pre-filled with commented-out examples.

### Config lookup order

1. `--config/-c`
2. `$REPOFLEET_CONFIG`
3. nearest `repofleet.toml` / `.repofleet.toml` walking up from the current directory
4. `%APPDATA%\repofleet\repofleet.toml` (Windows) or `~/.config/repofleet/repofleet.toml`

### Where repos end up

`--root` wins; otherwise `[workspace] root` (relative to the config file); with the default
`"auto"` the config's own folder is used when it already contains repos, and
`<config folder>/<directory>` when it does not. `repofleet status` prints the resolved root.

---

## Output & exit codes

```
config : C:\code\Backend Services\repofleet.toml
root   : C:\code\Backend Services
  [+] svc.api: updated
  [x] svc.auth: failed

--------------------------------------------------
Sync summary
--------------------------------------------------
svc.api   updated
          - on main
svc.auth  failed
          - git pull failed: ...
--------------------------------------------------
2 repo(s): 1 failed, 1 updated
```

| Action | Meaning |
| --- | --- |
| `cloned` | Freshly cloned. |
| `updated` | Pulled new commits. |
| `up to date` | Already current. |
| `skipped` | Nothing to do (already cloned, dirty with `--no-stash`, or dry run). |
| `missing` | Listed but not cloned yet. |
| `failed` | Git reported an error; details follow. |

Exit codes: `0` success · `1` a repo failed / nothing selected · `2` config or environment error ·
`130` interrupted.

---

## Authentication

repofleet uses your existing git credentials and sets `GIT_TERMINAL_PROMPT=0`, so a batch run fails
fast instead of hanging on a password prompt. Configure Git Credential Manager, a credential
helper, or SSH keys once before the first run.

---

## Troubleshooting

| Symptom | Fix |
| --- | --- |
| `'git' was not found on PATH` | Install git or open a fresh shell. |
| `repofleet: command not found` | Use `python -m repofleet ...`, or install with `pipx`. |
| `could not read Username` | Credentials are missing and prompts are disabled; clone one repo manually or use SSH. |
| `No repositories selected` | No config found — run `repofleet init`, or pass `--repo` / `--repos-file`. |
| Repos cloned somewhere unexpected | Check the `root :` header from `repofleet status`, or pass `--root`. |
| `stash pop failed - stash kept` | Resolve the conflict in that repo, then `git stash pop`. |

More cases in the [usage guide](docs/USAGE.md#17-troubleshooting).

---

## Safety notes

- Git is always invoked with an argument list, never through a shell.
- `GIT_TERMINAL_PROMPT=0` prevents a batch run from hanging on a credential prompt.
- Any credentials embedded in a remote URL are masked in console output.
- `pull --ff-only` is tried first; a merge pull is only attempted if fast-forward is impossible.
- If `git stash pop` fails after a pull, the stash is **kept** and the repo is reported as failed -
  your work is never discarded.
- `remove` only edits the config; it never deletes directories.

---

## Development

```bash
pip install -e ".[dev]"
pytest -q
```

Tests spin up real local git repositories in a temp folder — no network needed. See the
[usage guide](docs/USAGE.md#19-contributing--local-development) for the project layout and the
[publishing guide](docs/PUBLISHING.md) for cutting a release.

## License

MIT.
