What you'll achieve
By the end of this guide, you will know how to push, pull, fetch, and clone data between TerminusDB instances — and how to manage remotes, rebase branches, and resolve diverged histories.
Git-for-Data lets you transport data between TerminusDB instances using operations that mirror git: clone, push, pull, and fetch. You can collaborate on structured data by synchronising content repositories across cloud-hosted and local TerminusDB instances.
Prerequisites
- Two TerminusDB instances running (examples use
localhost:6363as local andlocalhost:6364as remote) - A database on the remote instance to clone from, or a local database with a remote configured
- Examples use
admin/mydbwith basic authentication
Remotes
Git-for-data operations use remotes — stored references to remote databases including branch information and layer state. One or more remotes can be added to a database.
Add a remote
Register a remote TerminusDB instance for push/pull operations:
curl -u admin:root -X POST http://localhost:6363/api/remote/admin/mydb \
-H "Content-Type: application/json" \
-d '{"remote_name": "origin", "remote_location": "http://localhost:6364/admin/mydb"}'Expected response:
{"@type": "api:RemoteResponse", "api:status": "api:success"}import TerminusClient from "@terminusdb/terminusdb-client";
const client = new TerminusClient.WOQLClient("http://localhost:6363", {
user: "admin",
key: "root",
organization: "admin",
db: "mydb",
});
await client.addRemote("origin", "http://localhost:6364/admin/mydb");from terminusdb_client import Client
client = Client("http://localhost:6363")
client.connect(user="admin", key="root", db="mydb")
client.add_remote("origin", "http://localhost:6364/admin/mydb")List remotes
curl -u admin:root "http://localhost:6363/api/remote/admin/mydb"Expected response:
{"@type": "api:RemoteResponse", "api:remote_names": ["origin"], "api:status": "api:success"}Show remote details
curl -u admin:root "http://localhost:6363/api/remote/admin/mydb?remote_name=origin"Expected response:
{"@type": "api:RemoteResponse", "api:remote_name": "origin", "api:remote_url": "http://localhost:6364/admin/mydb", "api:status": "api:success"}Update a remote URL
curl -u admin:root -X PUT http://localhost:6363/api/remote/admin/mydb \
-H "Content-Type: application/json" \
-d '{"remote_name": "origin", "remote_location": "http://newhost:6363/admin/mydb"}'Expected response:
{"@type": "api:RemoteResponse", "api:status": "api:success"}Delete a remote
curl -u admin:root -X DELETE "http://localhost:6363/api/remote/admin/mydb?remote_name=origin"Expected response:
{"@type": "api:RemoteResponse", "api:status": "api:success"}Clone a database
Clone creates a full copy of a database (schema, all branches, all layers) from one TerminusDB instance to another. A remote named origin is automatically configured in the new database.
curl -u admin:root -X POST http://localhost:6363/api/clone/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{
"comment": "Clone of remote mydb",
"label": "My Database",
"remote_url": "http://localhost:6364/admin/mydb"
}'Expected response:
{"@type": "api:CloneResponse", "api:status": "api:success"}The Authorization-Remote header provides credentials for the remote instance. The -u admin:root authenticates against the local instance.
await client.clonedb({
comment: "Clone of remote mydb",
label: "My Database",
remote_url: "http://localhost:6364/admin/mydb",
}, "admin", "mydb");client.clonedb("http://localhost:6364/admin/mydb", label="My Database")Clone direction matters
Clone pulls data from the remote_url into the local instance. If the remote cannot reach your local instance (e.g. behind a firewall), use reverse branch cloning instead.
Fetch
Fetch retrieves layer information from a remote and updates local references — without changing any local branch data. This tells your local instance what the remote looks like.
curl -u admin:root -X POST http://localhost:6363/api/fetch/admin/mydb/origin/_commits \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA=="Expected response:
{"@type": "api:FetchRequest", "api:status": "api:success", "api:head_has_changed": true, "api:head": "Layer_ID_abc123"}The path format is: /api/fetch/{organization}/{db}/{remote_name}/_commits
The Authorization-Remote header authenticates against the remote instance. The -u flag authenticates against the local instance.
const result = await client.fetch("origin");
console.log(result);
// { head_has_changed: true, head: "Layer_ID_abc123" }result = client.fetch("origin")
print(result)
# {"head_has_changed": True, "head": "Layer_ID_abc123"}Pull
Pull fetches remote changes and applies them to a local branch. Missing layers from the remote branch are transported and appended to the local branch.
curl -u admin:root -X POST http://localhost:6363/api/pull/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"remote": "origin", "remote_branch": "main"}'Expected response (new data pulled):
{"@type": "api:PullResponse", "api:status": "api:success", "api:head_has_changed": true, "api:head": "Layer_ID_def456"}Expected response (already up to date):
{"@type": "api:PullResponse", "api:status": "api:success", "api:head_has_changed": false}const result = await client.pull({
remote: "origin",
remote_branch: "main",
});
console.log(result);
// { head_has_changed: true, head: "Layer_ID_def456" }result = client.pull(remote="origin", remote_branch="main")
print(result)
# {"head_has_changed": True, "head": "Layer_ID_def456"}Push
Push sends local branch changes to a remote branch. Missing layers from the local branch are transported and appended to the remote.
curl -u admin:root -X POST http://localhost:6363/api/push/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"remote": "origin", "remote_branch": "main"}'Expected response (new data pushed):
{"@type": "api:PushResponse", "api:status": "api:success", "api:repo_head_updated": true, "api:repo_head": "Layer_ID_ghi789"}Expected response (already up to date):
{"@type": "api:PushResponse", "api:status": "api:success", "api:repo_head_updated": false, "api:repo_head": "Layer_ID_current"}const result = await client.push({
remote: "origin",
remote_branch: "main",
});
console.log(result);
// { repo_head_updated: true, repo_head: "Layer_ID_ghi789" }result = client.push(remote="origin", remote_branch="main")
print(result)
# {"repo_head_updated": True, "repo_head": "Layer_ID_ghi789"}Push requires linear history
Push succeeds only if the remote branch history is a strict ancestor of the local branch. If the remote has diverged (someone else pushed), you must pull and resolve first.
Push with prefixes
To also push prefix/context mappings along with data:
curl -u admin:root -X POST http://localhost:6363/api/push/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"remote": "origin", "remote_branch": "main", "push_prefixes": true}'Rebase
Rebase replays commits from one branch onto another. Both branches must share a common ancestor commit. Commit messages are retained.
curl -u admin:root -X POST http://localhost:6363/api/rebase/admin/mydb/local/branch/main \
-H "Content-Type: application/json" \
-d '{"author": "alice@example.com", "rebase_from": "admin/mydb/local/branch/feature"}'Expected response:
{
"@type": "api:RebaseResponse",
"api:status": "api:success",
"api:forwarded_commits": ["commit/abc123", "commit/def456"],
"api:rebase_report": [],
"api:common_commit_id": "commit/ancestor789"
}This replays all commits from feature onto main, starting from the common ancestor.
const result = await client.rebase({
rebase_from: "admin/mydb/local/branch/feature",
author: "alice@example.com",
});
console.log(result.forwarded_commits);
// ["commit/abc123", "commit/def456"]result = client.rebase(
"admin/mydb/local/branch/feature",
author="alice@example.com"
)
print(result["forwarded_commits"])
# ["commit/abc123", "commit/def456"]Complete workflow: clone, edit, push
This workflow demonstrates a full collaboration cycle — clone a database, make changes locally, and push them back:
# 1. Clone the remote database
curl -u admin:root -X POST http://localhost:6363/api/clone/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"comment": "Working copy", "label": "My Database", "remote_url": "http://remote:6363/admin/mydb"}'
# 2. Add a document locally
curl -u admin:root -X POST \
"http://localhost:6363/api/document/admin/mydb?author=alice&message=Add+new+product" \
-H "Content-Type: application/json" \
-d '{"@type": "Product", "name": "Widget", "price": 9.99}'
# 3. Push changes to remote
curl -u admin:root -X POST http://localhost:6363/api/push/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"remote": "origin", "remote_branch": "main"}'Complete workflow: pull remote changes
# 1. Fetch to see if remote has changed
curl -u admin:root -X POST http://localhost:6363/api/fetch/admin/mydb/origin/_commits \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA=="
# 2. Pull changes into local main
curl -u admin:root -X POST http://localhost:6363/api/pull/admin/mydb \
-H "Content-Type: application/json" \
-H "Authorization-Remote: Basic YWRtaW46cm9vdA==" \
-d '{"remote": "origin", "remote_branch": "main"}'
# 3. Verify the new data is present
curl -u admin:root "http://localhost:6363/api/document/admin/mydb?type=Product&as_list=true"Schema and instance separation
Push and pull transport instance data only — schema changes are not synchronised automatically. You must maintain schema consistency manually across instances.
To push schemas along with instance data, pass "push_prefixes": true in the push request (see Push with prefixes).
Operational technology environments
Git-for-Data is useful in environments with strict network segmentation (IEC 62443, Purdue model) where databases cannot maintain persistent connections. You can:
- Clone a database to an isolated environment
- Make changes offline
- Push changes back when connectivity is available
For environments where the cloud instance cannot reach the local instance, see Manual Reverse Branch Cloning and Transfer Data in Operational Technologies Landscapes.
Next steps
- How to Branch — create and manage branches locally
- How to Merge — merge branches with conflict detection
- Manual Reverse Branch Cloning — clone when cloud cannot reach local
- Transfer Data in OT Landscapes — network-segmented environments
- Version Control Operations Reference — complete API reference