db-git 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,363 @@
1
+ Metadata-Version: 2.4
2
+ Name: db-git
3
+ Version: 0.1.0
4
+ Summary: Keep your database in sync with your git branches.
5
+ Project-URL: Homepage, https://github.com/earthcomfy/git-db
6
+ Project-URL: Repository, https://github.com/earthcomfy/git-db
7
+ Project-URL: Issues, https://github.com/earthcomfy/git-db/issues
8
+ Project-URL: Changelog, https://github.com/earthcomfy/git-db/blob/main/CHANGELOG.md
9
+ Author: Hana Belay
10
+ License-Expression: MIT
11
+ License-File: LICENSE
12
+ Keywords: branching,cli,database,developer-tools,git,migrations,postgresql,snapshot
13
+ Classifier: Development Status :: 3 - Alpha
14
+ Classifier: Environment :: Console
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Operating System :: OS Independent
18
+ Classifier: Programming Language :: Python :: 3
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3.13
21
+ Classifier: Topic :: Database
22
+ Classifier: Topic :: Software Development :: Version Control :: Git
23
+ Classifier: Topic :: Utilities
24
+ Classifier: Typing :: Typed
25
+ Requires-Python: >=3.12
26
+ Requires-Dist: psycopg[binary]>=3.3.0
27
+ Requires-Dist: rich>=14.0.0
28
+ Requires-Dist: tomlkit>=0.14.0
29
+ Requires-Dist: typer>=0.24.1
30
+ Description-Content-Type: text/markdown
31
+
32
+ # git-db
33
+
34
+ [![CI](https://github.com/earthcomfy/git-db/actions/workflows/test.yml/badge.svg)](https://github.com/earthcomfy/git-db/actions/workflows/test.yml)
35
+ [![Python](https://img.shields.io/pypi/pyversions/git-db.svg)](https://pypi.org/project/git-db/)
36
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
37
+
38
+ Keep your database in sync with your git branches.
39
+
40
+ `git-db` is a developer tool for projects where database state follows code
41
+ changes: schema migrations, seed data, experimental feature work, and branch
42
+ switching during reviews. It installs a git `post-checkout` hook and keeps your
43
+ local database aligned with the branch you are working on.
44
+
45
+ > Status: PostgreSQL is supported today; support for additional database
46
+ > engines is planned.
47
+
48
+ ## Features
49
+
50
+ - Automatic database handling on `git checkout`
51
+ - Two workflows:
52
+ - `shared`: one database, saved and restored per branch
53
+ - `per-branch`: one database per branch
54
+ - PostgreSQL support today, with plans for more database backends
55
+ - Two PostgreSQL snapshot strategies:
56
+ - `template`: fast database clones using `CREATE DATABASE ... TEMPLATE`
57
+ - `pgdump`: portable snapshots using `pg_dump` and `pg_restore`
58
+ - Manual `save`, `restore`, `create`, `reset`, `list`, `status`, and `prune`
59
+ commands
60
+ - Safe hook behavior: checkout is never blocked by git-db failures
61
+ - Rich terminal output and local state stored under `.git/git-db/`
62
+
63
+ ## Quick Start
64
+
65
+ ```bash
66
+ uv tool install git-db # or pip install git-db
67
+ ```
68
+
69
+ Run this from inside a git repository:
70
+
71
+ ```bash
72
+ git-db init --database-url postgresql://postgres:postgres@localhost:5432/myapp
73
+ ```
74
+
75
+ Interactive init will ask:
76
+
77
+ - Whether to use `shared` or `per-branch` mode
78
+ - Whether to use `template` or `pgdump` strategy
79
+ - What to do when active connections block database operations
80
+ - Whether to install the git `post-checkout` hook
81
+
82
+ After setup, switch branches normally:
83
+
84
+ ```bash
85
+ git checkout feature/auth
86
+ ```
87
+
88
+ In `shared` mode, git-db saves the previous branch database and restores the
89
+ new branch snapshot if one exists.
90
+
91
+ In `per-branch` mode, git-db creates or selects a database named from the
92
+ current branch, for example:
93
+
94
+ ```text
95
+ myapp__feature__auth
96
+ ```
97
+
98
+ Because the database name changes per branch, your application server also
99
+ needs to connect to the branch database. For example, when working on
100
+ `feature/auth`, point your app's `DATABASE_URL` at `myapp__feature__auth`.
101
+
102
+ ## Choosing a Mode
103
+
104
+ ### Shared Mode
105
+
106
+ Shared mode keeps one database name from `DATABASE_URL`.
107
+
108
+ Use this when:
109
+
110
+ - You want one familiar local database name
111
+ - You want branch-specific snapshots
112
+ - You are comfortable with git-db dropping and restoring that local database
113
+ during branch switches
114
+
115
+ ### Per-Branch Mode
116
+
117
+ Per-branch mode creates a separate database for each branch. The configured
118
+ default branch keeps the original database name and acts as the seed database.
119
+
120
+ Use this when:
121
+
122
+ - You want branch databases to persist independently
123
+ - You prefer creating new databases over repeatedly restoring one shared
124
+ database
125
+
126
+ ## Choosing a Strategy
127
+
128
+ ### template
129
+
130
+ The `template` strategy uses PostgreSQL database cloning:
131
+
132
+ ```sql
133
+ CREATE DATABASE target TEMPLATE source;
134
+ ```
135
+
136
+ It is usually fast, but requires sufficient PostgreSQL privileges and can be
137
+ blocked by active connections to the source or target database.
138
+
139
+ ### pgdump
140
+
141
+ The `pgdump` strategy uses `pg_dump` and `pg_restore`.
142
+
143
+ It is slower than `template`, but can be a better fit when template cloning is
144
+ not available. It requires PostgreSQL client tools to be installed locally.
145
+
146
+ ## Commands
147
+
148
+ ### Initialize
149
+
150
+ ```bash
151
+ git-db init --database-url postgresql://user:password@localhost:5432/myapp
152
+ ```
153
+
154
+ Useful options:
155
+
156
+ ```bash
157
+ git-db init \
158
+ --database-url postgresql://user:password@localhost:5432/myapp \
159
+ --mode per-branch \
160
+ --strategy template \
161
+ --on-active-connections terminate
162
+ ```
163
+
164
+ Skip hook installation:
165
+
166
+ ```bash
167
+ git-db init --database-url postgresql://localhost/myapp --no-hook
168
+ ```
169
+
170
+ ### Inspect State
171
+
172
+ ```bash
173
+ git-db status
174
+ git-db list
175
+ ```
176
+
177
+ ### Shared Mode Commands
178
+
179
+ Save the current branch database:
180
+
181
+ ```bash
182
+ git-db save
183
+ ```
184
+
185
+ Restore the current branch database:
186
+
187
+ ```bash
188
+ git-db restore
189
+ ```
190
+
191
+ Save or restore a specific branch:
192
+
193
+ ```bash
194
+ git-db save main
195
+ git-db restore feature/auth
196
+ ```
197
+
198
+ ### Per-Branch Commands
199
+
200
+ Create a branch database before checking out the branch:
201
+
202
+ ```bash
203
+ git-db create feature/auth
204
+ ```
205
+
206
+ Drop and recreate a branch database from the seed database:
207
+
208
+ ```bash
209
+ git-db reset feature/auth
210
+ ```
211
+
212
+ The default branch database cannot be reset because it is the seed for other
213
+ branch databases.
214
+
215
+ ### Prune Deleted Branches
216
+
217
+ Preview stale snapshots or branch databases:
218
+
219
+ ```bash
220
+ git-db prune --dry-run
221
+ ```
222
+
223
+ Remove stale snapshots or branch databases:
224
+
225
+ ```bash
226
+ git-db prune --yes
227
+ ```
228
+
229
+ ### Hook Management
230
+
231
+ Install or reinstall the checkout hook:
232
+
233
+ ```bash
234
+ git-db hook install
235
+ ```
236
+
237
+ Remove the checkout hook:
238
+
239
+ ```bash
240
+ git-db hook remove
241
+ ```
242
+
243
+ Temporarily disable git-db without removing the hook:
244
+
245
+ ```bash
246
+ git-db disable
247
+ git-db enable
248
+ ```
249
+
250
+ You can also skip hook behavior for a single checkout:
251
+
252
+ ```bash
253
+ GIT_DB_SKIP=1 git checkout other-branch
254
+ ```
255
+
256
+ ## Configuration
257
+
258
+ `git-db init` writes `.git-db.toml` at the repository root.
259
+
260
+ Example:
261
+
262
+ ```toml
263
+ database_url = "postgresql://postgres:postgres@localhost:5432/myapp"
264
+ mode = "per-branch"
265
+ default_branch = "main"
266
+ strategy = "template"
267
+ on_active_connections = "terminate"
268
+ ```
269
+
270
+ Supported configuration keys:
271
+
272
+ | Key | Description | Default |
273
+ | --- | --- | --- |
274
+ | `database_url` | Database connection URL | required |
275
+ | `mode` | `shared` or `per-branch` | `shared` |
276
+ | `default_branch` | Seed branch for per-branch mode | `main` |
277
+ | `strategy` | `template` or `pgdump` | required |
278
+ | `on_active_connections` | `terminate` or `fail` | `terminate` |
279
+ | `snapshot_dir` | Shared-mode snapshot metadata/dump directory | `.git/git-db/snapshots` |
280
+ | `max_snapshots` | Snapshot count kept by prune logic | `20` |
281
+ | `force_terminate_timeout_ms` | Active connection termination timeout | `5000` |
282
+
283
+ Configuration precedence:
284
+
285
+ 1. Built-in defaults
286
+ 2. `.git-db.toml`
287
+ 3. Environment variables
288
+ 4. CLI options
289
+
290
+ Environment variables:
291
+
292
+ ```bash
293
+ DATABASE_URL
294
+ GIT_DB_DATABASE_URL
295
+ GIT_DB_MODE
296
+ GIT_DB_STRATEGY
297
+ GIT_DB_ON_ACTIVE_CONNECTIONS
298
+ GIT_DB_SNAPSHOT_DIR
299
+ GIT_DB_MAX_SNAPSHOTS
300
+ GIT_DB_FORCE_TERMINATE_TIMEOUT_MS
301
+ ```
302
+
303
+ `GIT_DB_DATABASE_URL` takes precedence over `DATABASE_URL`.
304
+
305
+ ### Active connections block an operation
306
+
307
+ Stop your development server, database console, migration watcher, or GUI
308
+ client, then retry.
309
+
310
+ Alternatively, configure:
311
+
312
+ ```toml
313
+ on_active_connections = "terminate"
314
+ ```
315
+
316
+ Your PostgreSQL user may need superuser privileges or membership in
317
+ `pg_signal_backend` to terminate sessions owned by other users.
318
+
319
+ ### Temporarily skip git-db
320
+
321
+ ```bash
322
+ git-db disable
323
+ git checkout some-branch
324
+ git-db enable
325
+ ```
326
+
327
+ Or for one command:
328
+
329
+ ```bash
330
+ GIT_DB_SKIP=1 git checkout some-branch
331
+ ```
332
+
333
+ ### Show full tracebacks
334
+
335
+ ```bash
336
+ GIT_DB_DEBUG=1 git-db status
337
+ ```
338
+
339
+ ## Development
340
+
341
+ Install dependencies:
342
+
343
+ ```bash
344
+ uv sync --group dev
345
+ ```
346
+
347
+ Run checks:
348
+
349
+ ```bash
350
+ uv run ruff check .
351
+ uv run mypy src tests
352
+ uv run pytest tests/unit
353
+ ```
354
+
355
+ Run the full nox suite:
356
+
357
+ ```bash
358
+ nox
359
+ ```
360
+
361
+ ## License
362
+
363
+ MIT
@@ -0,0 +1,30 @@
1
+ git_db/__init__.py,sha256=kUR5RAFc7HCeiqdlX36dZOHkUI5wI6V_43RpEcD8b-0,22
2
+ git_db/config.py,sha256=9mE8Pwc6iFnc6wfX_9HjzpJkwSGQNtYcp6c3mcDfPAI,7414
3
+ git_db/db.py,sha256=FA3VWaG7Avm6NyXP4eFwPJAP2TLF2bxa416eGalSrSs,533
4
+ git_db/errors.py,sha256=_KvV20_N3PP9ESETvsl0L4k55y8rl8F22AVpYb-w9q0,754
5
+ git_db/git.py,sha256=G-XW-xEt97Wh5TEsyVTH_0NX0UbvPhIa0NDzcXwPyvw,10057
6
+ git_db/hook_script.py,sha256=KEA1xt77J8IvS0SbpqMLgwgV4y2fgK7BA6wWBEHDqaA,1434
7
+ git_db/state.py,sha256=42_ikKGJG6f52sohk6fz-nAqhOLOnB5ZVLkiLvA4NYg,2568
8
+ git_db/storage.py,sha256=Fid6jepNM1-8lYTX-324WXEkk7Bma3HubSxXjKz7k6s,5592
9
+ git_db/backends/__init__.py,sha256=onyhOXPXR_eh38C4WJDAzLu-J0CJP8cuW4vchnS80CA,4020
10
+ git_db/backends/postgresql/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
+ git_db/backends/postgresql/backend.py,sha256=uaYRnb1JMeT0ER2xNrfEwRBd1dDsFA2Tug9X0r2lUYg,6026
12
+ git_db/backends/postgresql/branch_db.py,sha256=PEA1QTz7U3Vkkx1S8N9Gl2gqYPUecejNGBVPQ3dadTo,5726
13
+ git_db/backends/postgresql/connections.py,sha256=gR3IV4MTQlfBNo8lR78a8HJMvAqE1-kKdZ_NgqGnPM0,2453
14
+ git_db/backends/postgresql/pgdump.py,sha256=-epWSQvSypV6BGK2hyeO6gul_JIdEbsglA4hs7OH0BA,5629
15
+ git_db/backends/postgresql/template.py,sha256=fexuoTy0IKrs8V1zDIyJO1L8ZSpTDSAYeP5jw70pgug,4490
16
+ git_db/cli/__init__.py,sha256=ZLn5HbrLOokWC1rt7QY2hUPaj55w2SC7sGVCi4Pa1Rk,225
17
+ git_db/cli/_common.py,sha256=uyzlafCaB-tsUrRq25UmboujblA5v3g_9jLCBRU3zqQ,1042
18
+ git_db/cli/_console.py,sha256=nK7JES1yT7oi1iRMuPQY8P9oHttD4WMDd8l2ZoqEF6s,409
19
+ git_db/cli/_format.py,sha256=g1CEy-E8rz_sfRhRxwcYlCg0SsvUK6FZZgUQQNM54pg,1616
20
+ git_db/cli/_prompts.py,sha256=lfknOgNHNAPWkMi4RRIbrVbn6RZIgMqzRyb7ta5pLE4,3076
21
+ git_db/cli/branch.py,sha256=jtBA-i-TR7HE-dYj_Azxa1yJMwGXoGDaS7hao3S1h3Y,5377
22
+ git_db/cli/hook.py,sha256=-DW_0sOo7hHot5j0aqSISsmZAmFrns-XsyYJb5Xv85U,2640
23
+ git_db/cli/init.py,sha256=fsQhka7DdMj3i1vdWro9lVxBF1oB0eKQMh0J6Qc57qQ,9248
24
+ git_db/cli/inspect.py,sha256=ma_bF53Sy6DFN3orgF88wXyHKdZlrcbCIgGyLL5xb14,12457
25
+ git_db/cli/snapshot.py,sha256=5OjMdwr97Ga2HqK6xLf2sXNJbH6iAzyW5ZhEYUjpNSo,3713
26
+ db_git-0.1.0.dist-info/METADATA,sha256=VLNZ4dZ_nqdRfLoWpqAtuOb4xWJM3-iCGVBoWQGihp8,8451
27
+ db_git-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
28
+ db_git-0.1.0.dist-info/entry_points.txt,sha256=dU0NWsfYR8-COUVsowXR795CsqRqSFE-yBijHSX-wN8,42
29
+ db_git-0.1.0.dist-info/licenses/LICENSE,sha256=JTB2YQpxa0QXq1mU53Qj0hi-CwzxrV4GWa0g4FTZl8Q,1067
30
+ db_git-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ db-git = git_db.cli:app
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Hana Belay
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
git_db/__init__.py ADDED
@@ -0,0 +1 @@
1
+ __version__ = "0.1.0"
@@ -0,0 +1,173 @@
1
+ from __future__ import annotations
2
+
3
+ import contextlib
4
+ import importlib
5
+ from pathlib import Path
6
+ from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
7
+ from urllib.parse import urlparse
8
+
9
+ from git_db.errors import ConfigError
10
+
11
+ if TYPE_CHECKING:
12
+ from git_db.config import GitDbConfig
13
+ from git_db.state import BranchDbEntry
14
+
15
+ _BACKEND_REGISTRY: dict[str, type[DatabaseBackend]] = {}
16
+ _BUILTIN_BACKENDS: dict[str, str] = {
17
+ "postgresql": "git_db.backends.postgresql.backend",
18
+ }
19
+
20
+
21
+ class DbCursor(Protocol):
22
+ """
23
+ Minimal cursor protocol for database query results.
24
+ """
25
+
26
+ def fetchone(self) -> tuple[Any, ...] | None: ...
27
+ def fetchall(self) -> list[tuple[Any, ...]]: ...
28
+
29
+
30
+ class DbConnection(Protocol):
31
+ """
32
+ Minimal connection protocol returned by connect_maintenance.
33
+ """
34
+
35
+ def execute(self, query: Any, params: Any = None) -> DbCursor: ...
36
+ def close(self) -> None: ...
37
+
38
+
39
+ @runtime_checkable
40
+ class SnapshotStrategy(Protocol):
41
+ """
42
+ Protocol for snapshot save/restore strategies.
43
+ """
44
+
45
+ name: str
46
+
47
+ def save(
48
+ self,
49
+ db_url: str,
50
+ branch: str,
51
+ snapshot_dir: Path,
52
+ config: GitDbConfig,
53
+ ) -> None: ...
54
+
55
+ def restore(
56
+ self,
57
+ db_url: str,
58
+ branch: str,
59
+ snapshot_dir: Path,
60
+ config: GitDbConfig,
61
+ ) -> None: ...
62
+
63
+ def cleanup(
64
+ self,
65
+ branch: str,
66
+ snapshot_dir: Path,
67
+ config: GitDbConfig,
68
+ ) -> None: ...
69
+
70
+
71
+ class BranchDbManager(Protocol):
72
+ """
73
+ Protocol for per-branch database operations.
74
+ """
75
+
76
+ def exists(self, name: str) -> bool: ...
77
+
78
+ def create(
79
+ self,
80
+ target: str,
81
+ source: str,
82
+ branch: str,
83
+ created_from: str,
84
+ git_dir: Path,
85
+ ) -> None: ...
86
+
87
+ def drop(
88
+ self,
89
+ name: str,
90
+ branch: str,
91
+ git_dir: Path,
92
+ ) -> None: ...
93
+
94
+ def list(
95
+ self,
96
+ git_dir: Path,
97
+ ) -> list[tuple[str, BranchDbEntry, bool]]: ...
98
+
99
+
100
+ @runtime_checkable
101
+ class DatabaseBackend(Protocol):
102
+ """
103
+ Protocol for database engine backends.
104
+ """
105
+
106
+ engine: str
107
+ max_identifier_length: int
108
+
109
+ def apply_url_defaults(
110
+ self, params: dict[str, str | int | None]
111
+ ) -> dict[str, str | int]: ...
112
+
113
+ def get_engine_version(self, url: str) -> int: ...
114
+
115
+ def detect_strategy(self, config: GitDbConfig) -> SnapshotStrategy: ...
116
+
117
+ def branch_db_manager(self, config: GitDbConfig) -> BranchDbManager: ...
118
+
119
+ def connect_maintenance(self, params: dict[str, str | int]) -> DbConnection: ...
120
+
121
+ def build_subprocess_env(self, params: dict[str, str | int]) -> dict[str, str]: ...
122
+
123
+ def check_permissions(self, url: str) -> object: ...
124
+
125
+ def database_exists(self, url: str, name: str) -> bool: ...
126
+
127
+
128
+ def register_backend(scheme: str, cls: type[DatabaseBackend]) -> None:
129
+ """
130
+ Register a backend class for a URL scheme.
131
+ """
132
+ _BACKEND_REGISTRY[scheme] = cls
133
+
134
+
135
+ def get_backend(url: str) -> DatabaseBackend:
136
+ """
137
+ Auto-detect and instantiate the correct backend from a database URL.
138
+ """
139
+ parsed = urlparse(url)
140
+ scheme = parsed.scheme or "postgresql"
141
+
142
+ if scheme in ("postgres", "postgresql"):
143
+ scheme = "postgresql"
144
+
145
+ if scheme not in _BACKEND_REGISTRY:
146
+ _try_import_backend(scheme)
147
+
148
+ if scheme not in _BACKEND_REGISTRY:
149
+ supported = ", ".join(sorted(_BACKEND_REGISTRY.keys())) or "none"
150
+ raise ConfigError(
151
+ f"Unknown database scheme '{scheme}'. Supported: {supported}."
152
+ )
153
+
154
+ return _BACKEND_REGISTRY[scheme]()
155
+
156
+
157
+ def _try_import_backend(scheme: str) -> None:
158
+ """
159
+ Attempt to import a built-in backend module to trigger registration.
160
+ """
161
+ module_name = _BUILTIN_BACKENDS.get(scheme)
162
+ if module_name:
163
+ with contextlib.suppress(ImportError):
164
+ importlib.import_module(module_name)
165
+
166
+
167
+ __all__ = [
168
+ "BranchDbManager",
169
+ "DatabaseBackend",
170
+ "SnapshotStrategy",
171
+ "get_backend",
172
+ "register_backend",
173
+ ]
File without changes