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 complete usage guide.
1. Check the tools
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.25 or newer and Node.js 24 or newer.
Create one disposable workspace for everything below:
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, or build the source checkout you are standing in:
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:
command -v gitone
gitone --version
gitone --help
Optional short command:
sudo ln -s "$(command -v gitone)" "$(dirname "$(command -v gitone)")/git1"
git1 --version
3. Install the VS Code extension
Install the gitone_<tag>_vscode.vsix published with the release you
installed:
gitone vscode install --vsix ./gitone_<tag>_vscode.vsix
Or build it from the same source checkout, where --force replaces an
already installed development build of the same version:
cd "$GITONE_SOURCE/editors/vscode"
npm install
npm test
npm run package -- --out gitone-<version>.vsix
gitone vscode install --vsix ./gitone-<version>.vsix --force
Either way, confirm the result:
code --list-extensions --show-versions | grep '^devidevio\.gitone@'
It should print devidevio.gitone@<version>.
4. Create a local-only project
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:
- Setup mode:
Wizard, which Enter selects - Default branch:
main - Repository
app, visibilitypublic, pathsREADME.mdandsrc/** - Leave its remote empty and store it in
shared - Add repository
notes, visibilityprivate, pathnotes/** - Leave its remote empty and store it in
local - Stop adding repositories, choose
appas owner if asked, then confirm
Inspect the result:
gitone repo validate
gitone repo list
gitone doctor
gitone status
gitone status --porcelain
gitone status --json
5. Stage, inspect and commit
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:
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
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.
A symbolic link inside one repository is an owned path of its own. GitOne stores the link and never looks through it:
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:
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
gitone branch
gitone branch feature/tutorial
gitone switch 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. The feature commit exists only in
app and is no longer visible after switching back to main.
Local-only repositories are skipped by fetch and pull; push is rejected because commits exist but no remotes are configured:
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
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.
cat > .gitone.yml <<YAML
version: 1
default_branch: main
repositories:
public:
visibility: public
remote: $GITONE_TUTORIAL_ROOT/remotes/public.git
paths:
- .gitignore
- .gitone.yml
- public/**
internal:
visibility: private
remote: $GITONE_TUTORIAL_ROOT/remotes/internal.git
paths:
- internal/**
YAML
printf '# Public project\n' > 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:
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:
gitone push all --yes
gitone status
8. Clone and synchronize
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:
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:
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:
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
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:
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.
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:
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 <id> 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:
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:
cd "$GITONE_TUTORIAL_ROOT/migration"
rm -rf .git
gitone status
gitone doctor
11. See it in VS Code
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.mdand 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:
gitone status
gitone doctor
12. Verify the safety boundary
An unassigned file must block status and every mutation:
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:
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.