# GitOne - full documentation > GitOne keeps public code and private files in one working directory and maps every file to exactly one Git repository. When it cannot tell where a file belongs, it stops. This file is the complete GitOne documentation in one place: the instruction block GitOne writes into a project's `AGENTS.md`, the usage guide, the hands-on tutorial and the error reference. It is generated from the same sources https://gitone.io renders, so it says what the repository says. The short index is at https://gitone.io/llms.txt. --- # Instructions for AI agents The canonical block, published by `gitone agents update` into the project `AGENTS.md`. An agent working in a GitOne project needs this and little else. This project uses GitOne: one working directory, several Git repositories, ownership decided by path in `.gitone.yml`. - Use `gitone`, never `git`, for status, staging, committing and pushing. There is no `.git/` in the project root. - `gitone status --json` is the machine-readable project state (schema `version: 1`). Exit code 0 means ordinary changes. Exit code 1 means one of two things: the document was written and its `issues` array explains why the project is unsafe, or GitOne refused before producing any output and only stderr carries the error code. Empty stdout on exit 1 is always the refusal, so read stderr instead of parsing. - Commands: `setup`, `clone`, `init`, `migrate`, `backup list`, `backup git`, `backup restore`, `repo validate`, `repo list`, `status`, `add`, `unstage`, `restore`, `commit`, `show`, `diff`, `log`, `branch`, `switch`, `fetch`, `pull`, `push`, `reconfigure`, `recover`, `abort`, `doctor`, `agents`, `agents update`, `vscode info`, `vscode install`. `gitone --help` prints the accepted syntax and flags. Unknown commands and flags fail with `CLI001`. - Never edit `.gitone/` by hand or stage or commit `.gitone.local.yml`. Edit `.gitone.local.yml` when private repository configuration needs to change; it must stay local. After changing repository names, remotes or the repository set, run `gitone reconfigure` to preview and apply the change. Incoming structural changes use `gitone reconfigure --pull` or `gitone reconfigure --switch `; declare renames with `--rename old=new`. - Start a branch everywhere with `gitone switch -c `; open an existing one with `gitone switch `, which creates a missing local branch from that repository's own fetched `origin/` and tracks it. Run `gitone fetch` first when a repository has not seen the branch yet; GitOne never fetches implicitly and never guesses a start point. - `gitone branch -d ` deletes one finished branch from every repository at once. Each repository has to consider it merged by native Git's own rule - contained in its configured upstream, or in `HEAD` where none is configured - decided from local refs only. There is no force option and no remote deletion; ask a human instead of deleting a refused branch with plain Git. - Do not bypass a refusal with plain Git automatically. Manual branch repair and divergence resolution are human decisions; the usage guide documents them at https://gitone.io/docs/usage#manual-git-repairs. Never move a path across repository boundaries to make an error go away. - A new file must be added to the `paths` of exactly one repository in `.gitone.yml` (public) or `.gitone.local.yml` (private) before it can be staged. `PATH001` means unassigned, `PATH002` means assigned twice. - Repository boundaries are build boundaries. Before one file references another - an import, an include, a build input - check whether both paths have the same owner, and check the references you added again before you hand the work back. A reference across repositories works in your working directory and breaks in any checkout that has only one of them, such as CI. GitOne never reads file contents, so it cannot catch this: report it and ask a human rather than moving the path. - `commit` with explicit paths commits the current working-tree version of exactly those managed files, like native `git commit `, and leaves every other staged change staged. A file Git does not know yet has to be staged once before it can be selected. - `restore ...` discards the unstaged changes of those files. `--staged` unstages them instead, `--worktree` names the working tree explicitly, and `--staged --worktree` makes index and file match that repository's own HEAD again, which also removes a selected file HEAD does not have. `--source HEAD` is the only accepted source; it is the default as soon as `--staged` is given. Every form except `--staged` alone destroys uncommitted work, so ask a human before running one. - A path covered by `rules.protected_paths` must never be staged or committed; ownership does not override that hard deny. - Valid patterns are exact paths such as `README.md`, `*` inside one path segment and at most one `**` segment, as in `src/**`, `docs/**/*.md` or `**/*.md`. Duplicate patterns are an error; overlapping ones are not, but a concrete path matching two repositories is `PATH002`. - Non-interactive pushes require `gitone push --yes`; without a terminal a push fails with `PUSH001` instead of asking. Ask a human before pushing. `push: disabled` repositories are never published, and a configured `require_clean_worktree` rule must also pass. - `LOCK001` has two forms and the message says which. When it names a running operation, wait and retry. When it says the lock cannot be attributed to this machine, no operation can be confirmed running and waiting never ends: stop retrying, run `gitone doctor`, and ask a human to follow https://gitone.io/docs/usage#repairing-an-unattributable-lock. Never remove a lock file yourself. On `REC001`, `recover` finishes an interrupted add, unstage, restore, commit, branch, switch, pull or reconfigure and `abort` undoes it. For an interrupted push both commands only report what reached the remotes. Never delete `.gitone/recovery/` by hand. If branch deletion recovery refuses changed or ambiguous state, preserve it and ask a human to follow https://gitone.io/docs/usage#resolving-an-interrupted-branch-deletion. - `gitone doctor` is read-only and safe to run at any time, including while another operation holds the lock. --- # GitOne usage guide GitOne keeps several Git repositories in one working directory. This guide covers setup, the daily workflow and the safety rules you need in practice. For a complete walkthrough against disposable repositories, use the [hands-on tutorial](https://gitone.io/docs/tutorial). For exact flags, run `gitone --help`. Error codes, causes and fixes live in the [error reference](https://gitone.io/errors). - [Configuration](#configuration) - [Interactive setup](#interactive-setup) - [Commands](#commands) - [The normal workflow](#the-normal-workflow) - [Branches and remotes](#branches-and-remotes) - [Creating or importing a project](#creating-or-importing-a-project) - [Recovery](#recovery) - [Security boundary](#security-boundary) ## Configuration A GitOne project is the nearest parent directory containing `.gitone.yml`. There is no `.git/` in the project root. | Path | Purpose | | --- | --- | | `.gitone.yml` | Committed public ownership map. | | `.gitone.local.yml` | Uncommitted private repositories, paths and remotes. | | `.gitone/` | Managed Git repositories, the project lock and recovery state. | `gitone init` adds `.gitone/` and `.gitone.local.yml` to `.gitignore`. Neither path can be owned, staged or committed. ### Example `.gitone.yml`: ```yaml version: 1 default_branch: main rules: protected_paths: - .env - secrets/** push: require_clean_worktree: true repositories: website: visibility: public remote: git@github.com:example/website.git paths: - .gitignore - .gitone.yml - README.md - src/** ``` `.gitone.local.yml`: ```yaml version: 1 repositories: notes: visibility: private push: disabled remote: git@internal.example.com:ops/notes.git paths: - notes/** ``` Validate both files as one effective configuration: ```bash gitone repo validate gitone repo list ``` ### Fields | Field | Meaning | | --- | --- | | `version` | Required; currently `1`. | | `default_branch` | Required in `.gitone.yml`; initial branch for new repositories. | | `rules.protected_paths` | Paths GitOne must never manage or publish. | | `rules.push.require_clean_worktree` | Refuse push while the working tree has changes. | | `repositories..visibility` | Required; `public` or `private`. | | `repositories..paths` | Required ownership patterns. | | `repositories..remote` | Optional shorthand for `remotes.origin`. | | `repositories..push` | Optional; `allowed` or `disabled`. | Repository names match `[a-z][a-z0-9_-]*`; `all` is reserved. GitOne validates branch and remote names with native Git. ### Path grammar | Pattern | Matches | | --- | --- | | `README.md` | One exact path. | | `*.md` | Markdown files in the project root. | | `src/**` | Everything below `src/`. | | `docs/**/*.md` | Markdown files directly and recursively below `docs/`. | | `**/*.md` | Markdown files anywhere, including the project root. | `*` stays inside one path segment. `**` matches complete path segments and may appear only once in a pattern. Git pathspecs and regular expressions are not supported. Every relevant file must match exactly one repository. GitOne refuses unassigned and multiply assigned paths instead of choosing an owner. A path covered by `protected_paths` is always refused, even if an ownership pattern also matches it. ### Merge rules `.gitone.local.yml` merges repositories and remotes into the public file. Local protected paths extend the public list, and local push rules may tighten but never weaken public rules. A local repository's `paths` replaces its public list; it does not extend it. ## Interactive setup Run the guided setup in an existing directory: ```bash gitone setup ``` Setup collects missing configuration, shows a preview and asks once before it initializes a new project or migrates an existing root repository. Existing configuration is validated and reused, never silently rewritten. After successful setup, GitOne can show the release VSIX download and installation command for VS Code. See the [README installation section](https://github.com/devidevio/gitone#install) for extension availability. The command needs an interactive terminal. With `NO_COLOR` or a basic terminal it uses stable line-oriented prompts instead of the interactive browser. ### Setup modes | Mode | Result | | --- | --- | | `Wizard` | Asks for repositories and owned paths, then initializes or migrates. | | `Template` | Writes commented configuration templates and stops. | `Wizard` is the default. Both modes can add the managed GitOne block to the project's `AGENTS.md`; the default answer is no. ### Template mode Template mode creates `.gitone.yml` and `.gitone.local.yml`, updates `.gitignore` and optionally updates `AGENTS.md`. It does not initialize or migrate repositories. Edit the files, validate them, then rerun setup: ```bash gitone repo validate gitone setup ``` ### Choosing the owned paths The wizard lists existing files and directories. Choose the smallest stable paths that describe ownership: `src/**` for a complete feature tree, or an exact path for a single project file. GitOne does not infer public or private ownership from filenames. The project files `.gitone.yml`, `.gitignore`, `README.md` and `LICENSE` need an owner like every other relevant file. `.gitone.local.yml` and `.gitone/` are reserved and never need one. ### Correcting the owned paths If validation reports an unassigned or multiply assigned path, return to the path selection in setup or edit the configuration directly. Setup keeps valid answers while you correct the ownership list. ### Cancellation and retry Cancelling before confirmation writes nothing. If setup fails after writing a valid configuration, fix the reported problem and rerun `gitone setup`; it reuses that configuration and retries the unfinished action. ## Commands GitOne exposes an allowlist. Unknown commands and flags are rejected rather than forwarded to Git. | Command | Purpose | | --- | --- | | `gitone setup` | Configure and initialize or migrate interactively. | | `gitone clone [directory]` | Create a project from a repository containing `.gitone.yml`. | | `gitone init` | Initialize managed repositories from existing configuration. | | `gitone migrate [--yes]` | Convert a root `.git/` and retain it as a backup. | | `gitone backup list` | List migration backups. | | `gitone backup git ...` | Inspect a migration backup with native Git. | | `gitone backup restore ...` | Restore a migration backup to the root. | | `gitone repo validate` | Validate the merged configuration. | | `gitone repo list` | List configured repositories and remotes. | | `gitone status [--porcelain \| --json]` | Show all repository states and changes. | | `gitone add [-A \| -u \| ...]` | Stage owned changes. | | `gitone unstage [-A \| ...]` | Unstage without changing files. | | `gitone restore [--source HEAD] [--staged] [--worktree] ...` | Discard file changes, staged changes or both. | | `gitone commit [...] <-m \| -F ->` | Commit all staged or selected files. | | `gitone diff [all \| ] [--staged]` | Show native patches by repository. | | `gitone log [all \| ] [-n ]` | Show recent commits. | | `gitone branch [ \| -d ]` | List branches, create one everywhere or delete a merged one everywhere. | | `gitone switch [-c] ...` | Switch every repository to one branch, creating it where it is missing. | | `gitone fetch [all \| ]` | Fetch `origin` remote-tracking refs. | | `gitone pull [all \| ] ...` | Fast-forward from `origin`. | | `gitone push [all \| ] [--yes]` | Push outgoing commits. | | `gitone reconfigure ...` | Apply configured repository additions, removals, renames or remote changes. | | `gitone show --repository --source -- ` | Read exact file content for editor integrations. | | `gitone vscode info --json` | Report the editor protocol and GitOne version. | | `gitone vscode install ...` | Install the VS Code extension. | | `gitone recover` | Finish an interrupted operation. | | `gitone abort` | Undo an interrupted operation. | | `gitone doctor` | Check project health without changing it. | | `gitone agents` | Check the managed `AGENTS.md` block. | | `gitone agents update` | Write the current managed `AGENTS.md` block. | Use `gitone --help` for the full command list, `gitone --help` for exact accepted arguments and `gitone --version` for the installed version. Commands return `0` on success and `1` on failure. `show` returns `2` when the requested path is absent from the selected source. ### Terminal presentation Color and progress rendering are automatic. Redirected output, `TERM=dumb` and `NO_COLOR` stay plain. Machine-readable output never contains presentation codes, and GitOne strips control sequences from repository-provided text in normal human output. ## The normal workflow Once configuration is valid and the repositories are initialized, the daily flow stays close to Git: ```bash gitone status gitone add -A gitone diff --staged gitone commit -m "Describe the change" gitone push ``` GitOne groups output by repository and routes each path to its configured owner. A commit touching several repositories creates one native commit in each affected repository with a shared `GitOne-Group` trailer. ### Selecting changes Paths are relative to the current directory. For `add`, `unstage` and `restore` a directory selects every managed change below it; `.` and `..` are ordinary directories here, so `..` selects the parent subtree. GitOne does not accept absolute paths, literal globs or Git pathspecs. Shell-expanded globs work when every resulting argument is a managed file or non-empty managed directory. ```bash gitone add src/app.ts docs/USAGE.md gitone add docs gitone unstage docs/USAGE.md gitone commit src/app.ts -m "Update app startup" ``` A commit without paths consumes the staged state of every affected repository. A commit with paths commits the current working-tree versions of exactly those known files and leaves unrelated staged changes staged. It never accepts a directory or `.`. You can delete a tracked file and remove its ownership rule in the same change. While the deletion is pending, GitOne routes the missing file to the repository that tracks it, including after staging. Stage both changes with `gitone add -u`, or name both paths in a selective commit. Existing files still require an ownership rule; conflicting repository assignments remain errors. ### Undoing changes Unstage without touching the working tree: ```bash gitone unstage src/app.ts gitone restore --staged src/app.ts ``` Discard working-tree changes and restore the index version: ```bash gitone restore src/app.ts ``` Discard staged and unstaged changes together, so the file matches the last commit of the repository owning it again: ```bash gitone restore --source=HEAD --staged --worktree src/app.ts ``` `--staged` and `--worktree` are the destinations; without either, the working tree is the destination. The source is the owning repository's own HEAD as soon as `--staged` is given and otherwise its index, and `--source HEAD` selects HEAD explicitly. The flags come before the paths, each at most once and in any order. Every form except `--staged` alone destroys uncommitted changes in the selected files. A selected path the source does not hold, such as a newly staged file, is removed from the destination. GitOne does not restore untracked files or remove directories, and it refuses a restored `.gitone.yml` that would add, remove or rename a repository or change a remote; use `gitone reconfigure` for that. ### Machine-readable status Use JSON for tools and porcelain for simple line processing: ```bash gitone status --json gitone status --porcelain ``` Both formats start with schema version `1` and report repositories, changes and issues deterministically. Ahead and behind are `null`/`?` until a local remote-tracking branch exists. Exit 0 means ordinary changes. Exit 1 has two causes and stdout tells them apart: either the document was written and its `issues` array explains why the project is unsafe, or GitOne refused before producing any output at all - no configuration, nothing initialized, an unreadable project - and only stderr carries the error code. Parse stdout when it is non-empty; treat empty output on exit 1 as the refusal it is and read stderr. The JSON document also contains `ignored` and `ignore_exceptions`. `ignored` lists the project-relative paths the project `.gitignore` files hide, evaluated by native Git. An entry ending in `/` is a collapsed directory and stands for everything below it except the exact tracked paths in `ignore_exceptions`. Porcelain omits both fields because ignored file names may contain its tab and newline delimiters. The VS Code extension uses only public GitOne commands. It does not inspect `.gitone/` or invoke Git directly. ### Editor integration contract Editor integrations use `status --json`, `vscode info --json` and `show`. `show` returns the exact bytes stored in `HEAD` or the index; use its exit code to distinguish missing content from a failed command. `vscode info --json` reports the contract as `protocol`, currently `1`. An integration checks it before anything else and refuses to run against a different number. Protocol version `1` remains fixed for the pre-release contract. Every array of the status document is required, so a status without `ignored` or `ignore_exceptions` is rejected instead of presenting an incomplete project state as complete. The VS Code extension colors the reported ignored paths with VS Code's own `gitDecoration.ignoredResourceForeground`; it never evaluates ignore rules itself. ## Branches and remotes ### Branches List project branches, create one in every repository, or delete a finished one from every repository: ```bash gitone branch gitone branch feature/auth gitone branch -d feature/auth ``` Branch creation does not switch. Repositories must have commits and agree on their current branch. `-d` (or `--delete`) deletes one local branch everywhere, and only when every repository agrees that it is safe to lose. Each repository is judged on its own, with native Git's own rule for `git branch -d`: the branch has to be contained in its configured upstream, or in `HEAD` where no upstream is configured. A branch that is merged in only part of the project is refused for the whole project, and every blocking repository is named together with the ref it was compared with. Only local refs decide this. GitOne does not fetch first, so a configured upstream this repository has never fetched stops the deletion instead of being quietly replaced by a comparison with `HEAD` - run the `gitone fetch ` the message names and try again. No remote branch is ever deleted, and there is no force option. If the branch is not merged, keep it until you have deliberately integrated or published the work to its comparison ref. The branch settings go with the branch, exactly like native Git. The refs, the reflogs and those settings are recorded before the first branch disappears, so an interrupted deletion is finished by `gitone recover` or completely undone by `gitone abort`. A branch that was recreated or moved outside GitOne meanwhile is never overwritten or deleted. Once an abort starts restoring branches, finish it with `gitone abort`; `recover` refuses to restart deletion. If a process stopped after recording deletion intent but the branch still exists, GitOne cannot prove whether it was recreated and refuses both commands for [manual resolution](#resolving-an-interrupted-branch-deletion). Preserve the recovery data until every participating repository has been reconciled. ### Switching branches ```bash gitone switch -c feature/auth gitone switch feature/auth ``` `-c` (or `--create`) starts a new feature: it creates `feature/auth` in every repository, each at the commit that repository currently has checked out, and then switches the whole project to it. It refuses when any repository already has the name or has no commit yet, so half a project is never left to be created by hand. Without `-c`, a repository that already has the branch keeps its own local ref. A repository missing it gets a new branch created from its own fetched `origin/feature/auth`, configured to track that ref. The start point is never guessed from another repository and never taken from `HEAD`, and GitOne does not fetch on its own. A repository with neither the branch nor a matching `origin` ref stops the whole switch and is named with its next step: `gitone fetch ` when it has an `origin`, otherwise [Repairing missing branches](#repairing-missing-branches). GitOne refuses staged or unstaged changes and never stashes, merges or discards work. Untracked files remain, unless checkout would overwrite one. If the target branch changes `.gitone.yml`, GitOne previews the policy change before checkout. Non-interactive callers must pass the acceptance flag named by `gitone switch --help` and the refusal message. Branch creation, upstream configuration and checkout are one recoverable operation. A failure returns the project to its starting state, or leaves recovery state for `gitone recover` and `gitone abort`. An undo removes only the refs and upstreams this switch created; a pre-existing branch, a pre-existing upstream and anything changed outside GitOne are kept. ### Fetching ```bash gitone fetch gitone fetch website ``` Fetch updates `origin` remote-tracking refs only. It does not merge, rebase or change working-tree files. Multi-repository fetch is sequential: refs fetched before a later failure stay updated. ### Pulling ```bash gitone pull gitone pull website ``` Pull performs fast-forward updates only. Before changing anything it checks all selected repositories, their incoming trees and any incoming policy change. Local staged or unstaged changes are refused. For diverged history, follow [the manual repair procedure](#resolving-diverged-history). Pull across several repositories is transactional for branches, indexes and working-tree files. Fetched remote-tracking refs are not rolled back. ### Incoming policy changes An incoming `.gitone.yml` or tracked `.gitignore` is untrusted input. GitOne validates the projected configuration and checks the incoming trees against it before `pull` or `switch` changes local files. Safe changes are previewed once. Structural repository changes are handled by `gitone reconfigure`; new unassigned incoming paths can be assigned only to the repository that introduces them. Exact non-interactive flags are shown by the command's `--help` and refusal message. ### Assigning new incoming paths When new incoming paths are the only ownership problem, pull or switch can propose exact assignments to the repository whose incoming commit contains them. The configuration change is shown before it is written and remains unstaged for review. ### Reconfiguring the repositories Use reconfigure after deliberately editing repository names, remotes or the repository set: ```bash gitone reconfigure gitone reconfigure --pull gitone reconfigure --switch feature/auth ``` GitOne previews additions, retirements, renames and remote changes before applying them. Renames must be declared explicitly; they are never inferred. See the [reconfiguration reference](https://github.com/devidevio/gitone/blob/main/docs/RECONFIGURATION.md) for the full safety contract and non-interactive forms. ### Adopting history into an empty repository A repository created without `origin` has no first commit. For new local work, create owned files, stage them and commit normally. If an existing remote already holds its history, do **not** create an unrelated first commit: pull cannot join unrelated histories, and adding a remote with `reconfigure` alone does not check out an already initialized repository. For an empty secondary repository named `notes`, use the existing retirement and addition workflow: 1. Run `gitone status` and `gitone log notes` to confirm that `notes` has no commits or staged changes. Move any local files it owns to a safe directory outside the project before continuing, and keep them until step 5. Leave the other repositories on one branch. 2. Save its configuration, then remove `notes` from `.gitone.yml` and from `.gitone.local.yml` if it appears there. At least one repository must remain; never remove the repository owning `.gitone.yml`. 3. Run `gitone reconfigure` and accept the retirement of the empty repository. Its metadata is retained under `.gitone/retired/`; no manual deletion is needed. 4. Restore the `notes` entry with its owned paths and the existing remote URL. Use `.gitone.local.yml` for private configuration. Run `gitone reconfigure` again. It now adds the repository, validates the incoming files and checks out the remote branch matching the project's current branch. 5. Run `gitone status` and `gitone doctor`. Compare any saved local files with the checked-out versions before copying edits back. Review and commit any public configuration changes separately. The remote must contain that branch and only paths assigned to `notes`. For a project consisting of a single empty repository, start in a new location with `gitone clone` if the remote commits `.gitone.yml`; keep the old folder until you have compared and preserved its local files. ### Manual Git repairs GitOne deliberately has no merge, rebase or per-repository branch creation. The following are explicit human repair procedures, not automatic fallbacks for ownership errors. Native Git bypasses GitOne's checks. Run the commands from the project root, replace `website` and branch names with the reported ones, and do not run other GitOne operations while a merge is unresolved. Never use native Git to push around a GitOne refusal. #### Repairing missing branches This is only needed for a start point GitOne does not choose itself. A new branch everywhere is `gitone switch -c feature/auth` only when no repository already has that name. If any repository already has it, do not retry with `-c`: repair only the missing branches. A repository whose `origin/feature/auth` was already fetched is created and tracked by `gitone switch feature/auth` without any manual step. Start with `gitone branch`. For each missing repository with an `origin`, fetch and let `gitone switch` take the branch from there: ```bash gitone fetch website gitone switch feature/auth ``` Only when no remote branch exists (including a repository without `origin`, where there is nothing to fetch) choose the intended starting commit explicitly. For a repository that should keep its current committed files on the new branch: ```bash git --git-dir=.gitone/repositories/website branch feature/auth HEAD ``` This requires an existing commit and only creates the missing ref; it does not check out files. After repairing each missing repository, run `gitone switch feature/auth`. GitOne validates the target trees before checkout. Finish with `gitone status` and `gitone doctor`. #### Resolving diverged history Use this when local and remote commits have diverged but share a common ancestor. First finish or safely save local edits, then require a clean `gitone status` and run `gitone fetch website`. Inspect both sides: ```bash git --git-dir=.gitone/repositories/website log --oneline --graph --left-right HEAD...origin/main git --git-dir=.gitone/repositories/website diff --stat HEAD...origin/main ``` Review the incoming paths and any configuration changes before merging. If this also changes repository structure or transfers ownership, stop and coordinate that change using the [reconfiguration reference](https://github.com/devidevio/gitone/blob/main/docs/RECONFIGURATION.md). For an ordinary content merge on `main`: ```bash git --git-dir=.gitone/repositories/website --work-tree=. merge --no-edit origin/main ``` If Git reports conflicts, edit the named files, stage **only those resolved paths** in this repository and finish the native merge: ```bash git --git-dir=.gitone/repositories/website --work-tree=. add -- src/conflicted-file.ts git --git-dir=.gitone/repositories/website --work-tree=. commit --no-edit ``` To cancel an unresolved merge instead: ```bash git --git-dir=.gitone/repositories/website --work-tree=. merge --abort ``` Then run `gitone status` and `gitone doctor`. Publish the resolved history through `gitone push website`, which checks the outgoing commits and tree. Do not use `--allow-unrelated-histories` or force-push to make a migrated snapshot replace an existing remote. Migration starts a new history: use an empty remote for that history, or keep working from a clone of the original. ### Push safety and partial failures ```bash gitone push gitone push website gitone push --yes ``` Before the first remote changes, GitOne checks push policy, `origin`, connectivity, fast-forward safety and ownership of outgoing history and the resulting tree. A failed preflight pushes nothing. Pure deletions do not require a current ownership rule: removing a published file and its rule can be committed and pushed together. Added or changed content in every outgoing commit is still checked, even if a later commit deletes it. Removing a file does not remove its content from Git history. Push across several remotes cannot be atomic. GitOne records each result; if a later remote fails, earlier successful pushes remain published. Rerun push to publish the rest. ## Creating or importing a project ### Cloning a project ```bash gitone clone git@github.com:example/website.git gitone clone git@github.com:example/website.git my-project ``` The bootstrap repository must commit `.gitone.yml`. Clone validates that configuration, adopts the bootstrap repository with its history, initializes the other configured repositories and checks out their default branches. The destination must not exist. GitOne builds the project in a temporary directory and publishes it only after validation succeeds. `.gitone.local.yml` is never committed, so repositories that exist only there cannot be discovered by clone. Add them locally afterwards and run `gitone reconfigure`. ### Migrating an existing repository From a directory with a root `.git/` and a valid `.gitone.yml`: ```bash gitone migrate ``` Migration creates empty managed repositories and leaves your files in place. It stages and commits nothing, and does not split the old history. The original `.git/` is copied to a verified backup below `.gitone/migration-backup/` before the root repository is removed. Use `gitone migrate --yes` only after reviewing the preview. On failure, GitOne restores the original root repository or leaves recovery instructions. ### What only lives in the backup afterwards The managed repositories start without commits or staged files. Earlier commits, branches, tags, reflogs and repository-local settings remain only in the retained backup. ### Listing, inspecting and restoring a backup ```bash gitone backup list gitone backup git -- log --oneline gitone backup restore ``` `backup git` supports the read-only native Git commands `log`, `show`, `diff` and `status`. Restore copies the selected backup back to `.git/`; it never deletes the retained backup and refuses to overwrite an existing root `.git/`. ## Recovery ### Locking, recovery and abort Only one mutating GitOne command runs at a time. If an operation is interrupted, subsequent writes stop and tell you to choose: ```bash gitone recover gitone abort ``` `recover` finishes the recorded operation. `abort` restores its recorded starting state. Both refuse to overwrite files, indexes or refs changed by something else after the interruption. For interrupted push, both commands only read remote refs and report what was published. They cannot roll back a remote update. `gitone doctor` reports lock and recovery state without changing it. ### Repairing an unattributable lock The lock file records the process, the host and the command that took it. GitOne reclaims one whose process has ended on this machine and waits for one whose process is alive. An empty file records no owner at all and is reclaimed the same way, so it needs nothing from you. A file naming **another host**, or one whose contents **cannot be read**, is the one case that is neither: there is no process here to wait for, so waiting never ends. `gitone doctor` reports that state as its own and changes nothing: ```text Locks ✗ LOCK001 the lock names no process this machine can check: gitone commit, PID 4711 on host other-machine no operation can be confirmed running, so waiting for it does not end ``` Removing the file is then the repair, and it is safe only once nothing owns it. Establish that first, in this order: 1. Make sure no GitOne command is running against this project - on this machine and on every other machine that reaches the same directory through a network, shared or synchronised filesystem. A lock naming another host usually means exactly that: a second machine sees this project. 2. Read what it claims with `cat .gitone/lock` and keep a copy of the line. It is the only record of which command was interrupted. 3. Run `gitone doctor` again and confirm it still reports the lock as unattributable rather than as a running operation. 4. Only then `rm .gitone/lock`. Remove nothing else, and leave `.gitone/recovery/` untouched. 5. Run `gitone doctor` once more. If it now reports `REC001`, the recorded command was interrupted: finish it with `gitone recover` or undo it with `gitone abort` as described above. Never delete `.gitone/recovery/` by hand. If `doctor` reports a running operation instead, none of this applies: wait for it. Do not move or remove lock files to bypass a running operation. ### Resolving an interrupted branch deletion Use this only when an interrupted `gitone branch -d` refuses recovery or abort because refs or branch settings changed, or deletion intent is ambiguous. Do not delete `.gitone/recovery/` or edit its state to bypass the refusal. The record covers **every participating repository**, not just the one named in the first error. This is a human repair procedure. Stop editors and other processes that write Git state; do not run GitOne writes during the repair. 1. Make a private backup of the entire project outside its working directory, including hidden files, `.gitone/`, `.gitone.local.yml` and uncommitted files. Verify the backup before changing anything. It contains private data. 2. Read `.gitone/recovery/state.json`. Confirm it is a branch deletion record (`command: "gitone branch"` with a `deleted` array). For **each** entry, record its `name`, full `commit`, `settings` and saved `reflog` (base64-encoded), along with the top-level `branch`. These are the pre-deletion snapshots; progress flags do not prove who now owns a ref. 3. Inspect each recorded repository, including ones not named in the error. Replace `website` and `feature/auth` below with the recorded names: ```bash git --git-dir=.gitone/repositories/website show-ref --verify refs/heads/feature/auth git --git-dir=.gitone/repositories/website reflog show refs/heads/feature/auth git --git-dir=.gitone/repositories/website config --local --get-regexp '^branch\.' ``` A missing ref or reflog can be the result of the interrupted deletion. Compare the current ref, reflog and settings with the saved snapshots. 4. Decide and reconcile the final state of **every** recorded branch. To undo, restore missing refs at their full recorded commits and restore their branch settings and reflogs. Preserve any newer ref, reflog or settings separately before replacing them; do not assume a ref at the same commit is the original. To finish deletion, first inspect and preserve any newly created work, then explicitly remove only the branches and settings you chose to delete. Do not change HEAD, indexes or working files. If any comparison or restoration is unclear, keep the recovery record and [ask for help](https://github.com/devidevio/gitone/issues) with a redacted description; do not upload the project backup or raw recovery data. 5. Check every recorded repository against those decisions. Only when all refs, settings and reflogs have the intended state, move the entire `.gitone/recovery/` directory into your external private backup under a new, unused name. **Archive it; do not delete it.** This acknowledges the completed manual repair and removes the pending-operation block. Do not move or remove lock files to bypass a running operation. 6. Run `gitone branch`, `gitone status` and `gitone doctor`. Confirm the branch state and that no recovery is pending. Keep the backup and archived record until you have verified normal work can resume. ### Error codes Every failure starts with a stable error code. Use the [GitOne error reference](https://gitone.io/errors) for its meaning, usual cause and next action. The original message remains authoritative for the exact path, repository or command involved. Useful diagnostics: ```bash gitone doctor gitone repo validate gitone status --json gitone --help ``` ## Instructions for AI agents `gitone setup` can add a managed GitOne block to the root `AGENTS.md`. Maintain it non-interactively with: ```bash gitone agents gitone agents update ``` Only the marked block is managed; surrounding project instructions are preserved. The block tells agents to use `gitone`, respect ownership and avoid cross-repository dependencies. Read the [canonical agent instructions](https://github.com/devidevio/gitone/blob/main/internal/agents/instructions.md) when you need the complete block without running the command. ## Security boundary GitOne enforces path ownership: - every relevant path belongs to exactly one repository - protected and reserved paths are refused - outgoing commits and resulting remote trees are checked before push - incoming trees and policy changes are checked before checkout - unsafe symbolic links are refused - unknown commands and flags are never forwarded to Git - repository-provided text cannot emit terminal control sequences in normal human-readable output GitOne does not inspect file contents. It is not a secret scanner, data classifier or access-control boundary between files on the same machine. A secret inside an allowed public path can still be published. GitOne also trusts the local Git binary, Git configuration, credential helpers, hooks and Git LFS filters. Multi-remote push and fetch are not atomic. Symbolic links are supported only as safe internal aliases: relative, inside the project, resolving without another link, and owned by the same repository as their target. Absolute, escaping, dangling, chained, cyclic, cross-repository and reserved-path links are refused. Submodules, arbitrary Git command forwarding, history splitting during migration, Windows and general Git-compatible symlink support are not implemented. See [Deferred](https://github.com/devidevio/gitone/blob/main/README.md#deferred) for the current scope. --- # GitOne hands-on tutorial Install GitOne and run every supported command against disposable projects. Use one terminal session on macOS or Linux. For details beyond this walkthrough, see the [usage guide](https://gitone.io/docs/usage). ## 1. Check the tools ```bash git --version code --version ``` GitOne requires Git 2.28 or newer, and VS Code 1.127 or newer for the extension. Building from source instead of using a release additionally needs Go 1.26 or newer and Node.js 24 or newer. Create one disposable workspace for everything below: ```bash export GITONE_TUTORIAL_ROOT="$(mktemp -d)" export GIT_AUTHOR_NAME=GitOne export GIT_AUTHOR_EMAIL=gitone@example.invalid export GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME" export GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL" echo "$GITONE_TUTORIAL_ROOT" ``` ## 2. Install GitOne Install a release as described in the [README](https://gitone.io/#install), or build the source checkout you are standing in: ```bash export GITONE_SOURCE="$(pwd)" go test ./... go build -o gitone ./cmd/gitone sudo install -m 0755 ./gitone /usr/local/bin/gitone hash -r ``` Either way, confirm what you installed: ```bash command -v gitone gitone --version gitone --help ``` Optional short command: ```bash sudo ln -s "$(command -v gitone)" "$(dirname "$(command -v gitone)")/git1" git1 --version ``` ## 3. Install the VS Code extension Install the `gitone__vscode.vsix` published with the release you installed: ```bash gitone vscode install --vsix ./gitone__vscode.vsix ``` Or build it from the same source checkout, where `--force` replaces an already installed development build of the same version: ```bash cd "$GITONE_SOURCE/editors/vscode" npm install npm test npm run package -- --out gitone-.vsix gitone vscode install --vsix ./gitone-.vsix --force ``` Either way, confirm the result: ```bash code --list-extensions --show-versions | grep '^devidevio\.gitone@' ``` It should print `devidevio.gitone@`. ## 4. Create a local-only project ```bash mkdir -p "$GITONE_TUTORIAL_ROOT/local-project/src" mkdir -p "$GITONE_TUTORIAL_ROOT/local-project/notes" cd "$GITONE_TUTORIAL_ROOT/local-project" printf '# Local GitOne test\n' > README.md printf 'application\n' > src/app.txt printf 'private notes\n' > notes/plan.txt gitone setup ``` Use these answers in the setup wizard: 1. Setup mode: `Wizard`, which Enter selects 2. Default branch: `main` 3. Repository `app`, visibility `public`, paths `README.md` and `src/**` 4. Leave its remote empty and store it in `shared` 5. Add repository `notes`, visibility `private`, path `notes/**` 6. Leave its remote empty and store it in `local` 7. Stop adding repositories, choose `app` as owner if asked, then confirm Inspect the result: ```bash gitone repo validate gitone repo list gitone doctor gitone status gitone status --porcelain gitone status --json ``` ## 5. Stage, inspect and commit ```bash gitone add README.md src/app.txt notes/plan.txt gitone diff --staged gitone show --repository app --source index -- README.md gitone unstage README.md gitone add -A gitone commit -m "Initial local project" gitone log all -n 3 gitone show --repository app --source head -- README.md gitone status ``` The commit spans both repositories and therefore receives one shared `GitOne-Group` trailer. Exercise updates and undo operations: ```bash printf '\ntemporary README change\n' >> README.md printf '\napplication update\n' >> src/app.txt printf 'new file\n' > src/new.txt printf '\nprivate update\n' >> notes/plan.txt gitone diff gitone diff app gitone add -u gitone diff all --staged gitone restore --staged src/app.txt gitone unstage -A gitone restore README.md printf '\nsecond attempt\n' >> src/app.txt gitone add src/app.txt printf '\nthird attempt\n' >> src/app.txt gitone restore --source=HEAD --staged --worktree src/app.txt gitone add -A printf 'Update local files\n' | gitone commit -F - gitone status ``` `add -u` does not stage the new `src/new.txt`. Neither `unstage` nor `restore --staged` changes working files. `restore README.md` deliberately discards only that unstaged test change. The last restore discards the staged and the unstaged change of `src/app.txt` in one step, so index and file match the last commit of the repository owning it again. A symbolic link inside one repository is an owned path of its own. GitOne stores the link and never looks through it: ```bash ln -s app.txt src/alias.txt gitone status gitone add src/alias.txt gitone commit -m "Alias the application file" gitone show --repository app --source head -- src/alias.txt ``` `show` prints the stored target `app.txt`, not the file behind it. A link that leaves its repository or the project is refused instead: ```bash ln -s ../notes/plan.txt src/plan.txt gitone add src/plan.txt rm src/plan.txt ln -s /etc/hosts src/hosts gitone add -A rm src/hosts ``` Both must report `PATH003`, naming the link, its stored target and the reason: the first target belongs to `notes`, the second is absolute. ## 6. Create and switch branches ```bash gitone branch gitone switch -c feature/tutorial printf '\nfeature change\n' >> src/app.txt gitone add src/app.txt gitone commit -m "Change feature branch" gitone log app -n 3 gitone switch main gitone branch gitone status ``` Both managed repositories move together. `switch -c` creates the branch in every repository at the commit it currently has checked out and moves the project onto it in one step; `gitone branch feature/tutorial` would only write the refs and leave every repository where it is. The feature commit exists only in `app` and is no longer visible after switching back to `main`. Later, on another machine, `gitone fetch` followed by `gitone switch feature/tutorial` opens the same branch: a repository that does not have it locally yet gets it from its own `origin/feature/tutorial` and tracks it. Clean a finished branch up with `-d`: ```bash gitone branch throwaway gitone branch -d throwaway gitone branch -d feature/tutorial ``` `throwaway` holds nothing that `main` does not, so it is deleted in every repository at once. The second command is refused: `feature/tutorial` still holds the feature commit in `app`, and one repository that would lose work is enough to stop the whole deletion. There is no force option - merge or publish the branch first. Local-only repositories are skipped by fetch and pull; push is rejected because commits exist but no remotes are configured: ```bash gitone fetch gitone pull gitone push --yes gitone recover gitone abort ``` The push error is expected. `recover` and `abort` report that no interrupted operation exists. ## 7. Create a project with disposable remotes ```bash mkdir -p "$GITONE_TUTORIAL_ROOT/remotes" mkdir -p "$GITONE_TUTORIAL_ROOT/remote-project/public" mkdir -p "$GITONE_TUTORIAL_ROOT/remote-project/internal" git init --bare -b main "$GITONE_TUTORIAL_ROOT/remotes/public.git" git init --bare -b main "$GITONE_TUTORIAL_ROOT/remotes/internal.git" cd "$GITONE_TUTORIAL_ROOT/remote-project" ``` Create the committed ownership map. The remotes are written as absolute paths: a real project uses URLs, and a relative path would be resolved from each managed repository below `.gitone/` rather than from the project, which the clone in section 8 could not follow. ```bash cat > .gitone.yml < public/README.md printf '# Internal notes\n' > internal/notes.md gitone repo validate gitone init gitone repo list gitone doctor gitone status ``` Create the first remote history: ```bash gitone add -A gitone commit -m "Initialize remote project" gitone push ``` Press Enter at `Continue? [y/N]` to verify that the default aborts without changing a remote. Then publish explicitly: ```bash gitone push all --yes gitone status ``` ## 8. Clone and synchronize ```bash cd "$GITONE_TUTORIAL_ROOT" gitone clone "$GITONE_TUTORIAL_ROOT/remotes/public.git" cloned-project cd "$GITONE_TUTORIAL_ROOT/cloned-project" gitone repo list gitone log all -n 2 gitone status gitone doctor ``` The public repository bootstraps the project. Its committed `.gitone.yml` then tells GitOne to clone `internal` into the same working directory. Create a grouped update in the original project and publish one repository at a time: ```bash cd "$GITONE_TUTORIAL_ROOT/remote-project" printf '\npublic update\n' >> public/README.md printf '\ninternal update\n' >> internal/notes.md gitone add -u gitone commit -m "Update public and internal data" gitone push public --yes ``` Fetch and fast-forward only `public` in the clone: ```bash cd "$GITONE_TUTORIAL_ROOT/cloned-project" gitone fetch public gitone status gitone pull public gitone show --repository public --source head -- public/README.md ``` Publish and receive the remaining update: ```bash cd "$GITONE_TUTORIAL_ROOT/remote-project" gitone push internal --yes cd "$GITONE_TUTORIAL_ROOT/cloned-project" gitone fetch all gitone pull all gitone status gitone doctor ``` ## 9. Migrate an ordinary Git repository ```bash mkdir -p "$GITONE_TUTORIAL_ROOT/migration/src" mkdir -p "$GITONE_TUTORIAL_ROOT/migration/notes" cd "$GITONE_TUTORIAL_ROOT/migration" git init -b main printf 'legacy application\n' > src/app.txt printf 'legacy notes\n' > notes/plan.txt cat > .gitone.yml <<'YAML' version: 1 default_branch: main repositories: app: visibility: public paths: - .gitignore - .gitone.yml - src/** notes: visibility: private paths: - notes/** YAML git add -A git commit -m "Create legacy project" gitone migrate ``` Press Enter to test the safe default. The root `.git/` must remain. Then migrate and inspect the retained history: ```bash gitone migrate --yes test ! -e .git && echo "Root .git removed" gitone backup list gitone backup git -- log --oneline --all gitone status gitone doctor ``` The managed repositories intentionally start empty. The old history remains only in the verified migration backup, which `gitone backup` lists, reads and - with `gitone backup restore` - copies back to the root `.git/` whenever you want the old repository again. ## 10. Restore the migration backup The backup is not a one-way exit. Restoring it is an ordinary command, it changes nothing but the root `.git/`, and it can be undone again. ```bash cd "$GITONE_TUTORIAL_ROOT/migration" gitone backup restore ``` Press Enter to test the safe default: the answer is `No` and nothing changes. Then restore explicitly: ```bash gitone backup restore --yes git log --oneline --all ``` The old history is back at the project root, with every commit the legacy repository had. Pass `--backup ` from `gitone backup list` as soon as a project has more than one backup. While that root `.git/` exists the ordinary commands step aside, and the backup commands do not: ```bash gitone status gitone add -A gitone backup list gitone backup git -- log --oneline --all ``` The first two report `PATH003 .git: the project root is a Git repository` and exit `1`. The backup is still listed: restoring copies it, it never consumes it, so you can restore the same backup again later. Go back to the GitOne project by removing the root repository you just restored. Nothing else has to be undone - the managed repositories, the configuration and your files were never touched: ```bash cd "$GITONE_TUTORIAL_ROOT/migration" rm -rf .git gitone status gitone doctor ``` ## 11. See it in VS Code ```bash cd "$GITONE_TUTORIAL_ROOT/cloned-project" gitone vscode info --json code . ``` In the opened trusted workspace: - Open Source Control and run **GitOne: Refresh**. - Append a line to `public/README.md` and confirm that it appears automatically. - Open its diff, stage it, unstage it and stage it again. - Enter a message and run **GitOne: Commit & Push**. - Run **GitOne: Show Output**, **Recover Interrupted Operation** and **Abort Interrupted Operation**. Finish in the terminal: ```bash gitone status gitone doctor ``` ## 12. Verify the safety boundary An unassigned file must block status and every mutation: ```bash cd "$GITONE_TUTORIAL_ROOT/cloned-project" printf 'unassigned\n' > unassigned.test gitone status gitone add -A gitone push --yes rm unassigned.test gitone status ``` The first three GitOne commands must report `PATH001`. Unsupported commands must report `CLI001` without changing the project: ```bash gitone reset --hard gitone tag tutorial gitone status ``` ## Completed - [ ] Binary built and installed - [ ] VS Code extension built and installed - [ ] Local setup and every local file operation exercised - [ ] Safe symbolic link committed, unsafe ones refused - [ ] Branches created and switched synchronously - [ ] Push, clone, fetch and pull exercised against two remotes - [ ] Ordinary Git repository migrated with a verified backup - [ ] Migration backup restored and the restored root repository removed again - [ ] VS Code Source Control workflow exercised - [ ] Path ownership and unsupported-command safety verified The fixtures remain below `$GITONE_TUTORIAL_ROOT` for inspection. Delete that single disposable directory when finished. --- # GitOne error codes Every GitOne failure prints its code as the first token of the message. Each one below states what it means, what usually causes it and what to do next. Nothing here is a warning - use the original message together with the entry below to see what changed and how to continue. ### Configuration #### CONFIG001 - configuration missing Usually: Neither this directory nor any parent contains a `.gitone.yml`, so there is no GitOne project here. A project is always the nearest directory above you that has one. Do this: Run `gitone setup` to create a project, or change into a directory inside an existing one. #### CONFIG002 - invalid YAML Usually: The file parses as broken YAML, contains duplicate keys, holds several YAML documents, or has a field GitOne does not know. Unknown fields are errors on purpose - a typo must not be silently ignored. Do this: The message names the line. Fix it and run `gitone repo validate` until it passes. #### CONFIG003 - invalid configuration Usually: The YAML is well-formed but the configuration is not usable: a duplicate ownership pattern, an unsupported pattern such as `src/[ab].md` or `docs/**/**/*.md`, a repository name outside `[a-z][a-z0-9_-]*`, the reserved name `all`, a `version` other than `1`, a repository with no `paths`, `remote` and `remotes.origin` disagreeing, or a `default_branch` or `remotes` key that native Git itself refuses. Do this: The message names the exact conflict. Patterns are exact paths like `README.md`, `*` inside one path segment and at most one `**` segment, as in `src/**`, `docs/**/*.md` or `**/*.md`. `default_branch` and every remote name are checked with `git check-ref-format` before anything is created, so `bad branch`, `-dash` and `feature.lock` are rejected here rather than during `gitone init`. ### Paths and ownership #### PATH001 - path is not assigned to any repository Usually: A relevant file matches no ownership pattern in any repository. This is the most common GitOne error and it is the tool doing its job: it does not know where the file belongs, so it stops. Do this: Add the path to the `paths` of exactly one repository - `.gitone.yml` for public, `.gitone.local.yml` for private - or add it to `.gitignore` if it should not be tracked at all. #### PATH002 - path is assigned to more than one repository Usually: A file matches ownership patterns in two repositories at once. There is no priority rule and no most-specific-match rule, so GitOne refuses instead of picking one. Non-identical patterns may overlap in the configuration; the conflict appears when a concrete path matches both. Do this: Adjust or remove one of the reported ownership patterns so the path matches exactly one repository, then rerun the command. #### PATH003 - path is otherwise unsafe Usually: A path is protected by `rules.protected_paths`, or it is something GitOne will not manage: a nested or bare Git repository, a non-regular file, a name containing ASCII control characters, a case-insensitive collision with another path, or one of the reserved paths `.gitone/` and `.gitone.local.yml` reaching an index or a commit. A symbolic link also lands here unless it is a safe internal alias like `.claude/skills -> ../.agents/skills`: a relative target that resolves inside the project, exists as a regular file or a real directory, is reached without passing through another link, and belongs to the same repository as the link. Absolute, escaping, dangling, empty, cross-repository, chained and cyclic links are refused, as is any link that is or reaches `.gitone.yml`, `.gitone.local.yml`, `.gitignore`, `.gitone/`, `.git/` or a protected path. Do this: The message states which of these it is, and for a link it names the link, its stored target and the concrete reason. A protected path is a hard deny that ownership never overrides - move the file or remove the protection deliberately. A link and its target must be selected and changed together, so a partial `add`, `unstage` or `restore` is refused and nothing changes. ### Repositories #### REPO001 - repository invalid or unavailable Usually: The configuration is valid but the managed repositories are not usable: none is initialized yet, some are missing, the metadata below `.gitone/repositories/` is not a GitOne repository, the repositories are on different branches, or a named target has no configured `origin`. Do this: The message names the command that makes the project usable - `gitone init` on a fresh project, `gitone migrate` when a root `.git/` exists, `gitone switch ` when the branches disagree, and `gitone doctor` for anything else. A push is refused as a whole when any participating repository has no `origin`, and the message names the three ways out: add that remote, set `push: disabled` for a repository that is meant to stay on this machine, or push another repository by name. #### GIT001 - native Git failure Usually: The underlying `git` command failed and GitOne is reporting it rather than interpreting it. Authentication, a failing hook, a full disk, a missing object, an unreadable configuration. Do this: The message contains the `git` invocation as it actually ran - including the temporary `GIT_INDEX_FILE` when the command staged into one - and the output Git produced. Fix what that output reports, the credential, the hook, the full disk, and then repeat the original `gitone` command. Do not replay the `git` line by hand: on its own it runs outside the checks and the recovery GitOne wraps around it, and a staging command replayed without its temporary index would write the real one instead. Where a native repair genuinely is the answer, follow the documented procedure. [Manual Git repairs](https://gitone.io/docs/usage#manual-git-repairs) #### BRANCH001 - branch listing, creation or deletion refused or failed Usually: The name breaks native Git branch-name rules, a repository has no commit on its current branch, a repository is not on a branch, the repositories are on different branches, or one already has the branch. For `-d`, a repository does not have the branch, is on it, or does not consider it merged by native Git's own rule - contained in its configured upstream, or in `HEAD` where none is configured. Do this: Every problem is reported at once, with the ref each repository was compared with, and nothing was changed. Commit first if a repository is unborn; use `gitone switch` to put all repositories on the same branch or to leave the branch you want to delete. A deletion reads local refs only, so run the `gitone fetch ` the message names when a configured upstream is missing. There is no force option: keep the branch until you have deliberately integrated or published its work to the comparison ref. #### SWITCH001 - switch refused or failed Usually: A repository does not have the branch, is not on a branch, has staged or unstaged changes, the target tree contains a path the repository does not own, or the checkout would overwrite an existing working-tree file. Do this: Commit or undo your changes first - GitOne never stashes, discards or merges to make a switch possible. Use `gitone switch -c ` only when no repository already has that branch. A branch missing from only some repositories is created from each one's own `origin/`, so run the `gitone fetch ` the message names and switch again. If the branch is still absent after fetching, or there is no `origin`, follow Repairing missing branches below to choose the missing branch’s start point. [Repairing missing branches](https://gitone.io/docs/usage#repairing-missing-branches) #### RECONF001 - repository reconfiguration refused or failed Usually: `gitone reconfigure` was declined, hit a preflight refusal or failed while applying the accepted plan. Refusals include an unsafe remote URL or history, an addition that cannot safely check out or claim existing paths, an unsafe retirement, and an invalid or unsafe `--rename`. A later failure may leave fetched remote-tracking refs updated, keep recovery state when rollback or retirement preparation cannot finish safely, or occur after the reconfiguration completed when only the final move of retired metadata failed. Do this: Follow the exact repair in the message. Pass `--accept-repository-change` for a non-interactive run, declare a rename with `gitone reconfigure --rename =` - GitOne never infers one - and publish outstanding commits with `gitone push ` before retiring a repository. When the message includes `REC001` or says recovery state was kept, run `gitone recover` to finish or `gitone abort` to undo the reconfiguration. When it says the reconfiguration is complete, run the single `mv` it prints; otherwise the starting state was restored, apart from any fetched remote-tracking refs. ### Remote operations #### PUSH001 - push preflight failed Usually: One of the checks before the first remote is touched failed: a `push: disabled` repository was named explicitly, `require_clean_worktree` is set and the tree is dirty, `origin` is missing or has different fetch and push URLs, the remote is unreachable, the push would not be a fast-forward, an outgoing commit or the resulting tree contains a path the repository does not own - or there is no terminal to confirm on and `--yes` was not passed. Do this: Nothing was pushed. For the non-interactive case add `--yes`. For a non-fast-forward, follow Resolving diverged history in the usage guide linked below, then publish through `gitone push`. [Resolving diverged history](https://gitone.io/docs/usage#resolving-diverged-history) #### FETCH001 - fetch failed for one or more repositories Usually: A remote could not be contacted or refused the ref listing - authentication, transport, an unreachable host. Do this: A fetch across repositories is not atomic. The report above the error names which repositories already updated their remote-tracking refs; rerun `gitone fetch` to retry the rest. #### PULL001 - pull refused or failed Usually: A repository that would move has staged or unstaged changes, its branch does not track exactly `origin/`, the remote-tracking branch is missing after the fetch, the branch has diverged so no fast-forward is possible, or the incoming tree contains a path the repository does not own. Do this: A pull is fast-forward only and no flag changes that. Follow Resolving diverged history in the usage guide linked below for an explicit human repair. For a repository without its first commit, use Adopting history into an empty repository; creating an unrelated local commit does not adopt remote history. [Resolving diverged history](https://gitone.io/docs/usage#resolving-diverged-history) [Adopting history into an empty repository](https://gitone.io/docs/usage#adopting-history-into-an-empty-repository) #### CLONE001 - clone refused or failed Usually: The destination already exists, the bootstrap repository has no committed `.gitone.yml` on its default branch, more than one or no repository owns that file, the owning repository’s configured `origin` is not exactly the URL you cloned, a configured `origin` lacks the default branch, or an incoming tree contains a path its repository does not own. Do this: Nothing was created and no existing path was changed. Clone the URL the committed configuration names, and clone into a path that does not exist yet. ### Locking, recovery and setup #### LOCK001 - the project lock is unavailable Usually: Only one mutating operation runs at a time. Two different states share this code and the message says which one it is: an operation is currently running, and the message names its process ID and command - or the lock file names another host, or cannot be read, and then no running operation can be confirmed at all. Do this: For a running operation, wait for it to finish and retry; `gitone doctor` and `gitone repo validate` are read-only and work while the lock is held. For a lock that cannot be attributed to this machine, waiting never ends, because there is no local process to wait for: `gitone doctor` reports that as its own state, and the linked procedure establishes that nothing owns the lock before anything is removed. Never remove a lock file to get past an operation that really is running. [Repairing an unattributable lock](https://gitone.io/docs/usage#repairing-an-unattributable-lock) #### REC001 - an interrupted operation must be recovered first Usually: A previous add, unstage, restore, commit, branch, switch or pull was interrupted and left recorded state under `.gitone/recovery/`. GitOne refuses to start anything new on top of it. Do this: Run `gitone recover` to finish it, or `gitone abort` to undo it. Never delete `.gitone/recovery/` by hand. If branch deletion recovery refuses changed or ambiguous refs or settings, preserve the record and follow the linked human procedure for all participating repositories. For an interrupted push, both commands only report what actually reached the remotes. [Resolving an interrupted branch deletion](https://gitone.io/docs/usage#resolving-an-interrupted-branch-deletion) #### MIG001 - migration refused, failed or interrupted Usually: The project is not a plain root repository - a `.git` file rather than a directory, linked worktrees, submodules, a bare repository, a nested repository, an already migrated project - or a previous migration was interrupted. `gitone backup` also reports a missing, ambiguous, unknown, unsafe or invalid backup selection and every refused or failed restoration with this code. Do this: For an interruption, `gitone migrate` offers to restore the verified backup instead of migrating again. For a backup command, run `gitone backup list` and pass the full ID as `--backup `; a restore additionally needs the root `.git` path to be absent. The backup is always kept; it is never moved or consumed. #### SETUP001 - interactive setup refused Usually: `gitone setup` asks questions, so it needs an interactive terminal on standard input. It also refuses inside another GitOne project or another Git repository, on an invalid existing configuration, on an interrupted migration, or on repository state under `.gitone/` that neither init nor migrate explains. Do this: Run it in a real terminal. To set a project up non-interactively, write `.gitone.yml` yourself and run `gitone init` or `gitone migrate`. #### AGENT001 - AGENTS.md missing, not current or not safely editable Usually: `gitone agents` found no `AGENTS.md`, one without the GitOne instructions, or a marked block that is no longer the current text. `gitone agents update` reports it too when the file is one GitOne must not edit by itself: a symbolic link or another non-regular file, recognizable GitOne text that is neither marked nor exactly the current block, duplicate blocks, marked and unmarked instructions together, or missing and repeated markers. Do this: Run `gitone agents update` for a missing, incomplete or outdated block. For an unclear file, edit it by hand until it holds exactly one block between `` and ``, then run `gitone agents` again. ### Command line #### CLI001 - unsupported command or flag Usually: GitOne runs an allowlist and forwards nothing to `git`. `reset`, `tag`, `merge`, `rebase`, `stash`, Git pathspecs, globs, `--` separators and most Git flags are rejected here - as are `-h`, `gitone help`, and any argument after `--help`. Bare `gitone` and a command group called without a subcommand are not refusals: they print their help and exit `0`. Do this: Every refusal is followed by one hint line naming the page that lists the accepted form - `gitone --help` when the input names a known command, `gitone --help` otherwise. Do not automatically bypass a refusal with plain `git`. The usage guide’s Manual Git repairs section documents deliberate human repairs; publishing still goes through GitOne’s ownership checks. [Manual Git repairs](https://gitone.io/docs/usage#manual-git-repairs) #### SHOW001 - requested path absent from HEAD or index Usually: The request was valid but the named path does not exist in the selected source. This exits `2`, not `1`, so an editor integration can tell an absent version from a broken command. Do this: Usually nothing is wrong: a file that is untracked has no HEAD version, and a file that is not staged has no index version. #### VSCODE001 - VS Code CLI or extension installation failed Usually: The `code` command is not on `PATH`, the named VSIX does not exist, the extension is already installed and `--force` was not passed, or VS Code’s own CLI reported a failure. Do this: Install a downloaded VSIX with `gitone vscode install --vsix `. If `code` is missing, open VS Code’s command palette and run `Shell Command: Install code command in PATH`, then reopen the terminal. [Extension installation and availability](https://github.com/devidevio/gitone#install)