page.tsx258 lines · main
1import { DocsShell } from '../../../components/shell';
2
3export const metadata = {
4 title: 'doltgres version control',
5};
6
7export default function DoltgresVersionControlPage() {
8 return (
9 <DocsShell>
10 <h1 className="font-mono text-2xl tracking-tight">version control</h1>
11 <p className="mt-2 font-mono text-sm text-[var(--color-text-muted)]">
12 DoltGres is Postgres that branches, commits, and merges like Git — but the
13 unit of versioning is the whole database, not a file. Every operation is a
14 SQL function you call with <code>SELECT</code>.
15 </p>
16
17 <h2 className="mt-12 font-mono text-lg">the model: working → staged → committed</h2>
18 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
19 Like Git, DoltGres tracks three tiers of change. A normal{' '}
20 <code>INSERT</code>/<code>UPDATE</code>/<code>DELETE</code> lands in the{' '}
21 <strong className="text-[var(--color-text)]">working set</strong> — your
22 uncommitted edits. You <strong className="text-[var(--color-text)]">stage</strong>{' '}
23 the changes you want to keep, then{' '}
24 <strong className="text-[var(--color-text)]">commit</strong> them to permanent,
25 named history.
26 </p>
27 <ul className="mt-3 list-inside list-disc font-mono text-sm text-[var(--color-text-muted)]">
28 <li>
29 <strong className="text-[var(--color-text)]">working set</strong> — uncommitted,
30 unstaged edits. Each branch has its own working set, so changes stay isolated.
31 </li>
32 <li>
33 <strong className="text-[var(--color-text)]">staged</strong> — changes added with{' '}
34 <code>dolt_add</code>, queued for the next commit.
35 </li>
36 <li>
37 <strong className="text-[var(--color-text)]">committed</strong> — a permanent,
38 hashed snapshot of the <em>entire</em> database state, linked to its parent commit.
39 </li>
40 </ul>
41
42 <Callout title="gotcha: a Dolt commit is NOT a SQL transaction COMMIT">
43 These are two different things that both happen to be called &ldquo;commit&rdquo;.
44 A SQL <code>COMMIT</code> just ends a database transaction — it makes your writes
45 durable in the working set. A <strong className="text-[var(--color-text)]">Dolt
46 commit</strong> (<code>SELECT dolt_commit(...)</code>) creates a versioned snapshot
47 in history with an author, message, and content hash. You can run a thousand SQL
48 transactions and still have zero Dolt commits. To capture a point in history, you
49 must take a Dolt commit explicitly.
50 </Callout>
51
52 <h2 className="mt-12 font-mono text-lg">the golden rule: SELECT, never CALL</h2>
53 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
54 DoltGres speaks the PostgreSQL dialect, and Postgres allows side-effects inside a{' '}
55 <code>SELECT</code> (the same way <code>nextval()</code> does). So every mutating
56 version-control operation is a <strong className="text-[var(--color-text)]">function
57 invoked with <code>SELECT</code></strong>:
58 </p>
59 <Snippet>{`SELECT dolt_commit('-a', '-m', 'add notes table'); -- DoltGres (Postgres)`}</Snippet>
60 <p className="mt-1 font-mono text-xs text-[var(--color-text-subtle)]">
61 If you have seen MySQL-flavored Dolt, this is the one thing to unlearn: MySQL Dolt
62 uses <code>CALL dolt_commit(...)</code>. DoltGres does not — it is always{' '}
63 <code>SELECT dolt_xxx(...)</code>.
64 </p>
65
66 <h2 className="mt-12 font-mono text-lg">the mutating functions</h2>
67 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
68 Each returns a small result — a status, and/or a commit hash and message. The core
69 set:
70 </p>
71 <div className="mt-3 overflow-x-auto rounded-md border border-[var(--color-border-subtle)]">
72 <table className="w-full border-collapse font-mono text-xs">
73 <thead>
74 <tr className="border-b border-[var(--color-border-subtle)] bg-[var(--color-surface)] text-left text-[var(--color-text)]">
75 <th className="p-3">function</th>
76 <th className="p-3">example</th>
77 <th className="p-3">returns</th>
78 </tr>
79 </thead>
80 <tbody className="text-[var(--color-text-muted)]">
81 <FnRow fn="dolt_add" ex="SELECT dolt_add('-A');" ret="status" />
82 <FnRow fn="dolt_commit" ex="SELECT dolt_commit('-a','-m','msg');" ret="hash" />
83 <FnRow fn="dolt_checkout" ex="SELECT dolt_checkout('-b','feat');" ret="status, message" />
84 <FnRow fn="dolt_branch" ex="SELECT dolt_branch('-c','main','feat');" ret="status" />
85 <FnRow
86 fn="dolt_merge"
87 ex="SELECT dolt_merge('feat','--no-ff','-m','msg');"
88 ret="hash, fast_forward, conflicts, message"
89 />
90 <FnRow fn="dolt_reset" ex="SELECT dolt_reset('--hard','<hash>');" ret="status" />
91 <FnRow fn="dolt_tag" ex="SELECT dolt_tag('v1','HEAD');" ret="status" />
92 </tbody>
93 </table>
94 </div>
95 <p className="mt-2 font-mono text-xs text-[var(--color-text-subtle)]">
96 The same <code>SELECT dolt_xxx(...)</code> convention covers the rest of the surface
97 too — <code>dolt_revert</code>, <code>dolt_cherry_pick</code>, <code>dolt_rebase</code>,{' '}
98 <code>dolt_stash</code>, <code>dolt_clean</code>, and{' '}
99 <code>dolt_verify_constraints</code>.
100 </p>
101
102 <h2 className="mt-12 font-mono text-lg">end-to-end: branch → change → commit → merge</h2>
103 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
104 A realistic workflow. Make a feature branch, edit data on it, commit the snapshot,
105 switch back to <code>main</code>, and merge the branch in.
106 </p>
107 <Snippet>{`-- 1. create a feature branch off main and switch to it
108SELECT dolt_checkout('-b', 'add-welcome-note');
109
110-- 2. make changes (ordinary SQL — these land in the working set)
111INSERT INTO notes (id, body) VALUES ('n_001', 'welcome to briven');
112
113-- 3. stage everything and commit a snapshot of the whole database
114SELECT dolt_add('-A');
115SELECT dolt_commit('-m', 'seed welcome note'); -- returns the new commit hash
116
117-- 4. go back to main
118SELECT dolt_checkout('main');
119
120-- 5. merge the feature branch in (--no-ff forces a real merge commit)
121SELECT dolt_merge('add-welcome-note', '--no-ff', '-m', 'merge welcome note');`}</Snippet>
122 <p className="mt-2 font-mono text-xs text-[var(--color-text-subtle)]">
123 <code>dolt_merge</code> returns a row with <code>hash</code>,{' '}
124 <code>fast_forward</code>, <code>conflicts</code>, and <code>message</code>. If{' '}
125 <code>conflicts</code> is non-zero, the merge stopped and is waiting for you to
126 resolve them (see below). A fast-forward merge — where <code>main</code> had no new
127 commits since the branch point — moves the pointer forward and creates no merge
128 commit unless you pass <code>--no-ff</code>.
129 </p>
130
131 <h2 className="mt-12 font-mono text-lg">reading state: system tables</h2>
132 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
133 Writes are functions; reads are tables. DoltGres exposes version-control state as
134 tables in the <code>dolt</code> schema, each with a{' '}
135 <code>dolt_</code>-prefixed alias you can use unqualified. This{' '}
136 <code>dolt.</code> schema namespace is the main DoltGres-specific divergence from
137 MySQL Dolt (which only has the <code>dolt_</code> prefixed names).
138 </p>
139 <Snippet>{`-- what is staged / unstaged right now
140SELECT * FROM dolt.status;
141
142-- every branch, its head commit, latest committer + message
143SELECT * FROM dolt.branches;
144
145-- commit history reachable from the current HEAD
146SELECT * FROM dolt.commits WHERE date < '2026-01-01';
147
148-- the same tables are reachable via the dolt_ aliases:
149SELECT * FROM dolt_status;
150SELECT * FROM dolt_branches;`}</Snippet>
151
152 <h2 className="mt-12 font-mono text-lg">conflicts</h2>
153 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
154 DoltGres merges cell-by-cell. A conflict happens when both branches changed the{' '}
155 <em>same</em> cell to different values. Unlike Git, conflicts are not written as{' '}
156 inline <code>&lt;&lt;&lt;&lt;&lt;</code> markers in a file — they are recorded in a
157 per-table system table: <code>dolt_conflicts_&lt;table&gt;</code>. Each conflicted
158 cell shows three values:
159 </p>
160 <ul className="mt-3 list-inside list-disc font-mono text-sm text-[var(--color-text-muted)]">
161 <li><strong className="text-[var(--color-text)]">base</strong> — the original common-ancestor value</li>
162 <li><strong className="text-[var(--color-text)]">ours</strong> — the current branch&apos;s value</li>
163 <li><strong className="text-[var(--color-text)]">theirs</strong> — the merging branch&apos;s value</li>
164 </ul>
165 <Snippet>{`-- inspect the conflicts on a specific table
166SELECT * FROM dolt_conflicts_notes;
167
168-- resolve by keeping the current branch's version of every conflicted row
169SELECT dolt_conflicts_resolve('--ours', 'notes');
170
171-- ...or keep the incoming branch's version
172SELECT dolt_conflicts_resolve('--theirs', 'notes');`}</Snippet>
173 <Callout title="resolving conflicts does not guarantee a valid merge">
174 Merges happen at the storage layer, so combining two branches can still produce a
175 database that violates a constraint (for example a foreign-key reference that only
176 existed on one side). &ldquo;Zero conflicts&rdquo; is not the same as &ldquo;valid
177 merge.&rdquo; After resolving, run{' '}
178 <code>SELECT dolt_verify_constraints();</code> to surface any constraint violations
179 the merge introduced before you commit it.
180 </Callout>
181
182 <h2 className="mt-12 font-mono text-lg">info functions</h2>
183 <p className="mt-3 font-mono text-sm text-[var(--color-text-muted)]">
184 Read-only helpers for answering &ldquo;where am I?&rdquo; and &ldquo;how do these
185 two branches relate?&rdquo;
186 </p>
187 <Snippet>{`-- which branch is this session on?
188SELECT active_branch();
189
190-- the common-ancestor commit of two branches (their merge base)
191SELECT dolt_merge_base('main', 'add-welcome-note');`}</Snippet>
192
193 <h2 className="mt-12 font-mono text-lg">what to read next</h2>
194 <ul className="mt-3 flex flex-col gap-2 font-mono text-sm">
195 <NextLink
196 href="/doltgres/history"
197 title="history + time travel"
198 body="AS OF queries, the commit log, and per-table history, diff, and blame"
199 />
200 <NextLink
201 href="/undo"
202 title="undo + snapshots"
203 body="how briven turns commits and branches into one-click undo for your data"
204 />
205 <NextLink
206 href="/doltgres/limitations"
207 title="beta + limitations"
208 body="what doltgres does and does not support yet"
209 />
210 </ul>
211 </DocsShell>
212 );
213}
214
215function Callout({ title, children }: { title: string; children: React.ReactNode }) {
216 return (
217 <div className="mt-4 rounded-md border border-[var(--color-warning)] bg-[var(--color-warning)]/10 p-4 font-mono text-sm text-[var(--color-text-muted)]">
218 <p className="font-semibold text-[var(--color-warning)]">{title}</p>
219 <div className="mt-1">{children}</div>
220 </div>
221 );
222}
223
224function Snippet({ children }: { children: string }) {
225 return (
226 <pre className="mt-3 overflow-x-auto rounded-md border border-[var(--color-border-subtle)] bg-[var(--color-surface)] p-4 font-mono text-xs">
227 <code>{children}</code>
228 </pre>
229 );
230}
231
232function FnRow({ fn, ex, ret }: { fn: string; ex: string; ret: string }) {
233 return (
234 <tr className="border-b border-[var(--color-border-subtle)] last:border-0">
235 <td className="p-3 align-top text-[var(--color-text)]">
236 <code>{fn}</code>
237 </td>
238 <td className="p-3 align-top">
239 <code>{ex}</code>
240 </td>
241 <td className="p-3 align-top">{ret}</td>
242 </tr>
243 );
244}
245
246function NextLink({ href, title, body }: { href: string; title: string; body: string }) {
247 return (
248 <li>
249 <a
250 href={href}
251 className="block rounded-md border border-[var(--color-border-subtle)] bg-[var(--color-surface)] p-4 transition hover:border-[var(--color-border)]"
252 >
253 <p className="font-mono text-[var(--color-text)]">{title}</p>
254 <p className="mt-1 font-mono text-xs text-[var(--color-text-muted)]">{body}</p>
255 </a>
256 </li>
257 );
258}