Implement the following plan:
Store git objects and refs in Postgres tables. Two implementations: pure SQL (PL/pgSQL functions, works on managed Postgres) and a C extension (native types, faster parsing). A libgit2-based bridge program registers Postgres as the storage backend so standard git push/fetch/clone work against the database.
gitgres/ Makefile # top-level, delegates to sub-makes sql/ schema.sql # tables, indexes functions/ object_hash.sql object_read_write.sql tree_parse.sql commit_parse.sql ref_manage.sql views/ queryable.sql # materialized views ext/ # Postgres C extension Makefile # PGXS gitgres.c # PG_MODULE_MAGIC, init git_oid_type.c # 20-byte OID type with operators sha1_hash.c # C SHA1 via OpenSSL tree_parse.c # C tree entry parser gitgres.control sql/gitgres--0.1.sql # CREATE TYPE, CREATE FUNCTION backend/ # libgit2 pluggable backend Makefile main.c # CLI entry point (serves repos) odb_postgres.c # git_odb_backend implementation odb_postgres.h refdb_postgres.c # git_refdb_backend implementation refdb_postgres.h writepack_postgres.c # packfile receive handling import/ gitgres-import.sh # shell script: git plumbing + psql test/ test_helper.rb schema_test.rb object_hash_test.rb object_store_test.rb tree_parse_test.rb commit_parse_test.rb ref_test.rb roundtrip_test.rb # push then clone, diff working treesDepends on pgcrypto (available on all managed Postgres). OIDs stored as bytea (20 bytes raw).
CREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE repositories ( id serial PRIMARY KEY, name text NOT NULL UNIQUE, created_at timestamptz NOT NULL DEFAULT now());
-- Git objects: raw content without the git header.-- Type uses git's internal numbering: 1=commit, 2=tree, 3=blob, 4=tag.-- OID = SHA1("<type> <size>\0<content>"), computed on write.CREATE TABLE objects ( repo_id integer NOT NULL REFERENCES repositories(id), oid bytea NOT NULL, type smallint NOT NULL, size integer NOT NULL, content bytea NOT NULL, PRIMARY KEY (repo_id, oid));CREATE INDEX idx_objects_oid ON objects (oid);
CREATE TABLE refs ( repo_id integer NOT NULL REFERENCES repositories(id), name text NOT NULL, oid bytea, -- NULL for symbolic refs symbolic text, -- NULL for direct refs PRIMARY KEY (repo_id, name), CHECK ((oid IS NOT NULL) != (symbolic IS NOT NULL)));
CREATE TABLE reflog ( id bigserial PRIMARY KEY, repo_id integer NOT NULL REFERENCES repositories(id), ref_name text NOT NULL, old_oid bytea, new_oid bytea, committer text NOT NULL, timestamp_s bigint NOT NULL, tz_offset text NOT NULL, message text, created_at timestamptz NOT NULL DEFAULT now());CREATE INDEX idx_reflog_ref ON reflog (repo_id, ref_name, id);git_object_hash(type, content) -- SHA1 of <type> <size>\0<content> using pgcrypto digest()git_object_write(repo_id, type, content) -- compute hash, INSERT ON CONFLICT DO NOTHING, return oidgit_object_read(repo_id, oid) -- return (type, size, content)git_object_read_prefix(repo_id, prefix, prefix_len) -- abbreviated OID lookupgit_tree_entries(content) -- parse binary tree into (mode, name, entry_oid) rowsgit_commit_parse(content) -- parse commit into tree_oid, parent_oids[], author fields, committer fields, messagegit_ref_update(repo_id, name, new_oid, old_oid, force) -- compare-and-swap with SELECT FOR UPDATEgit_ls_tree_r(repo_id, tree_oid, prefix) -- recursive tree walk returning (mode, path, oid, type)CREATE MATERIALIZED VIEW commits_view ASSELECT o.repo_id, o.oid AS commit_oid, encode(o.oid, 'hex') AS sha, c.tree_oid, c.parent_oids, c.author_name, c.author_email, to_timestamp(c.author_timestamp) AS authored_at, c.committer_name, c.committer_email, to_timestamp(c.committer_timestamp) AS committed_at, c.messageFROM objects o, LATERAL git_commit_parse(o.content) cWHERE o.type = 1;
CREATE MATERIALIZED VIEW tree_entries_view ASSELECT o.repo_id, o.oid AS tree_oid, e.mode, e.name, e.entry_oidFROM objects o, LATERAL git_tree_entries(o.content) eWHERE o.type = 2;This is the bridge. Implement git_odb_backend and git_refdb_backend structs that store/retrieve from Postgres via libpq. libgit2 handles all protocol work (pack negotiation, delta resolution, ref advertisement).
odb_postgres.c)Implements these callbacks from git_odb_backend:
read(data, len, type, backend, oid) -- SELECT type, size, content FROM objects WHERE repo_id=$1 AND oid=$2read_header(len, type, backend, oid) -- SELECT type, size FROM objects WHERE ...read_prefix(full_oid, data, len, type, backend, short_oid, prefix_len) -- prefix match on oid columnwrite(backend, oid, data, len, type) -- INSERT INTO objects ... ON CONFLICT DO NOTHINGexists(backend, oid) -- SELECT 1 FROM objects WHERE ...exists_prefix(full_oid, backend, short_oid, prefix_len) -- prefix existence checkforeach(backend, cb, payload) -- SELECT oid FROM objects WHERE repo_id=$1, call cb for eachwritepack(writepack_out, backend, odb, progress_cb, payload) -- return a git_odb_writepack that accumulates pack bytes, then on commit delegates to libgit2's indexer to extract objects and calls write for eachfree(backend) -- close PG connectionThe backend struct holds a PGconn* and the repo_id.
refdb_postgres.c)Implements these callbacks from git_refdb_backend:
exists(exists_out, backend, ref_name) -- SELECT 1 FROM refs WHERE ...lookup(ref_out, backend, ref_name) -- SELECT oid, symbolic FROM refs WHERE ..., construct git_referenceiterator(iter_out, backend, glob) -- SELECT name, oid, symbolic FROM refs WHERE name LIKE ..., return custom iteratorwrite(backend, ref, force, who, message, old_id, old_target) -- CAS update using a transaction with SELECT FOR UPDATErename(ref_out, backend, old_name, new_name, force, who, message) -- UPDATE refs SET name=$1del(backend, ref_name, old_id, old_target) -- DELETE with CAS checkhas_log / ensure_log / reflog_read / reflog_write / reflog_rename / reflog_delete -- operate on the reflog tablelock / unlock -- use Postgres advisory locks (pg_advisory_xact_lock)free(backend) -- close connectionmain.c)A program that serves one or more repos. For testing, it can run as a one-shot helper that git invokes. Longer term it could serve HTTP smart protocol or the git:// protocol.
Initial approach: implement a git-remote-gitgres helper. When you do git clone gitgres::postgres://localhost/mydb/myrepo, git invokes git-remote-gitgres postgres://localhost/mydb/myrepo. The helper:
libgit2 has git_transport_register for custom transports, or we can use the simpler git_remote_callbacks approach.
ext/)The Postgres extension, separate from the libgit2 backend. Adds performance for SQL queries.
git_oid type: 20-byte fixed binary, hex I/O, btree + hash operator classes. Replaces bytea columns with a proper type so you can write WHERE oid = 'abc123...'.git_object_hash_c(type, content) RETURNS git_oid: C SHA1 via OpenSSL, ~10x faster than pgcryptogit_tree_entries_c(content) RETURNS SETOF (mode, name, oid): C pointer arithmetic instead of PL/pgSQL byte walkingBuilt with PGXS, links against OpenSSL.
import/gitgres-import.sh)Shell script that imports an existing git repo into the database using git plumbing commands and psql.
Usage: gitgres-import <repo-path> <conninfo> <repo-name>git -C <repo> rev-list --objects --all to get all object OIDsgit -C <repo> cat-file --batch to get type + contentgit -C <repo> show-ref to get all refs, INSERT into refs tableSchema + object hash + read/write functions -- the foundation. Verify that git_object_hash(3, 'hello') matches git hash-object --stdin.
Tree + commit parsing functions -- test against real objects from a git repo using git cat-file.
Import script -- import a small test repo (~50 commits). Verify object count matches git rev-list --objects --all | wc -l.
Materialized views -- REFRESH after import. Write example queries against commits and trees.
libgit2 ODB backend -- implement read/write/exists. Test by creating a libgit2 repo with the Postgres backend, writing objects through it, verifying they appear in the database.
libgit2 refdb backend -- implement CRUD + iterator. Test ref creation and lookup.
git-remote-gitgres helper -- wire up libgit2 transport so git push/clone work.
Roundtrip test -- push a repo, clone it elsewhere, diff the working trees.
C extension -- git_oid type, C hash, C tree parse. Benchmark against PL/pgSQL.
Scale test -- import homebrew-core.
Minitest. Each test connects to a gitgres_test database, runs schema setup in a BEGIN, rolls back in teardown. SQL functions tested by comparing output against git CLI on the same objects.
Roundtrip integration test: create temp repo with commits, push via the backend, clone into new directory, diff -r.
git_oid type.If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/andrew/.claude/projects/-Users-andrew-code-gitgres/0d86fef0-4a2d-473c-bb7e-29e502603055.jsonl
>pkg-config --cflags --libs libpq 2>/dev/null || echo "libpq not found via pkg-config"; pg_config --libdirNow I need to check a few more headers to understand the writepack and indexer interfaces.
Let me check a few more key headers and then start writing the C code and tests in parallel.
Now I have a good understanding of the libgit2 API. Let me also check the writepack structure and the reflog read/write APIs.
Let me check a few more headers I need for the writepack and reflog implementation.
Good, I have a clear picture of all the APIs now. Let me also check the git_odb_new non-experimental API and the repository wrapping API.
Now I have all the API details I need. Let me check the git_odb_add_backend function signature and the refdb creation.
Now let me check a couple more things and then write all the C files.
Now I have everything I need. Let me write all the C backend files, the import script, and the tests. I'll use parallel agents for the larger files.
The project is empty. I need to:
Let me check what's in the libgit2-inspect directory and also check if I need to install libgit2.
Let me also check psql availability and whether the test database exists.
Let me plan the work with task creation and then start implementing in order. This is a massive project so let me be systematic.
Let me start with installing libgit2 and checking the database, then implement everything in build order.
Good, the C backend files are written. Now let me write the import script, C extension, and tests in parallel.
>chmod +x /Users/andrew/code/gitgres/import/gitgres-import.sh>psql --version && psql -c "SELECT version();" postgres 2>&1 | head -5Now let me write all the test files and the C extension in parallel.
Good, all the files are written. Now let me verify everything compiles and the tests pass. Let me first check the pg gem is available, then run the tests.
>gem list pg 2>&1 | head -5>ls /Users/andrew/code/gitgres/backend/>ls /Users/andrew/code/gitgres/test/Let me first drop and recreate the test database cleanly, then run the tests.
>dropdb gitgres_test 2>/dev/null; createdb gitgres_test && psql -f sql/schema.sql gitgres_test 2>&1 && psql -f sql/functions/object_hash.sql gitgres_test 2>&1 && psql -f sql/functions/object_read_write.sql gitgres_test 2>&1 && psql -f sql/functions/tree_parse.sql gitgres_test 2>&1 && psql -f sql/functions/commit_parse.sql gitgres_test 2>&1 && psql -f sql/functions/ref_manage.sql gitgres_test 2>&1>ruby -Itest -e 'Dir.glob("test/*_test.rb").sort.each { |f| require_relative f }' 2>&1All 30 tests pass. Now let me try building the C backend.
>cd /Users/andrew/code/gitgres/backend && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1>ls -la /private/tmp/libgit2-inspect/Backend compiles. Let me now try building the C extension.
>cd /Users/andrew/code/gitgres/ext && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1Missing includes in sha1_hash.c. Let me fix it and also check tree_parse.c.
The sha1_hash.c is missing #include "utils/builtins.h" which provides VARSIZE_ANY_EXHDR, VARDATA_ANY, etc. Let me also use the EVP API since the legacy SHA1 functions are deprecated in OpenSSL 3.
>cd /Users/andrew/code/gitgres/ext && make clean 2>/dev/null; make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1Tons of OpenSSL warnings (from the -Wcast-function-type flag in PGXS) but I need to check if it actually linked. Let me look at the end more carefully.
>cd /Users/andrew/code/gitgres/ext && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | tail -20The issue is that VARDATA and SET_VARSIZE are still failing. The problem is that the PGXS build is compiling for both arm64 and x86_64 (-arch arm64 -arch x86_64). On one arch (likely x86_64) the builtins.h isn't resolving properly. Let me check what's happening more carefully.
Actually, looking at the errors more carefully: VARDATA(result) is returning int instead of void*. This suggests the macro isn't being properly resolved. This is actually a Postgres.app issue - the server headers for PG 16 built for universal binary have this problem with some macros.
I'm wondering if the postgres.h include path is incomplete or if I need to pull in additional headers to get VARDATA properly defined for the x86_64 target. to parameter of type 'void *'forVARDATA(result)`. The fact that it's returning int suggests the macro isn't expanded properly.
Wait, I see #include "utils/builtins.h" is included now. The issue might be that VARDATA is defined in postgres.h via varatt.h or varlena.h. Let me check what header defines VARDATA in PG 16.
Actually, in PG 16, VARDATA should be defined in postgres.h -> c.h -> varatt.h. Let me double-check.
Actually, the real issue might be the dual-architecture build. When building for x86_64 on arm64 mac, some headers might not resolve correctly. But more likely, there's a specific include missing. Let me just check by adding #include "varatt.h" explicitly, which was introduced in PG 16.
The issue is that PG 16 moved VARDATA macros to varatt.h. Let me fix the includes.
>brew list libgit2 2>/dev/null || echo "libgit2 not installed via homebrew">ls /Applications/Postgres.app/Contents/Versions/16/include/postgresql/server/varatt.h 2>/dev/null && echo "varatt.h exists">rm -f sha1_hash.o && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -E '(error:|gitgres\.so|^make:)' | head -20>rm -f *.o && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -v 'warning:' | grep -v '^ ' | grep -v '^$' | grep -v 'note:'This is a large project implementation. Let me break it down and start working through the build order systematically. Let me first understand what already exists in the repo, then start implementing.
Let me check the current state of the project first.
>rm -f *.o gitgres.so && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -cE '^.*error:' ; echo "---"; make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -E '(^gcc.*-o gitgres|error:|^make:)' | tail -5>make clean PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 && make PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -cE 'error:'; ls -la gitgres.so 2>/dev/null || echo "No .so file">ls -la gitgres.dylib 2>/dev/null || echo "No dylib"; ls -la *.o 2>/dev/null || echo "No .o files"Extension builds successfully (it's .dylib on macOS). Zero errors, just OpenSSL warnings from their headers. Let me now do a quick integration test - install the extension and verify it works.
>sudo make install PG_CONFIG=/Applications/Postgres.app/Contents/Versions/16/bin/pg_config 2>&1 | grep -v warning | grep -v note># Postgres.app allows installs without sudo if we target the right dir
PKGLIBDIR=$(/Applications/Postgres.app/Contents/Versions/16/bin/pg_config --pkglibdir)
SHAREDIR=$(/Applications/Postgres.app/Contents/Versions/16/bin/pg_config --sharedir)
echo "pkglib: $PKGLIBDIR"
echo "share: $SHAREDIR"
ls -la "$PKGLIBDIR" 2>&1 | head -3
cp gitgres.dylib "$PKGLIBDIR/" 2>&1
cp gitgres.control "$SHAREDIR/extension/" 2>&1
cp sql/gitgres--0.1.sql "$SHAREDIR/extension/" 2>&1
echo "Installed.">brew list libgit2 2>/dev/null || echo "libgit2 not installed via homebrew">ls -la /Applications/Postgres.app/Contents/Versions/16/lib/postgresql/_int.dylibOwned by andrew but "Operation not permitted" - likely SIP. Let me try with install.
>PKGLIBDIR=$(/Applications/Postgres.app/Contents/Versions/16/bin/pg_config --pkglibdir)
SHAREDIR=$(/Applications/Postgres.app/Contents/Versions/16/bin/pg_config --sharedir)
install -m 755 gitgres.dylib "$PKGLIBDIR/gitgres.dylib" 2>&1
install -m 644 gitgres.control "$SHAREDIR/extension/gitgres.control" 2>&1
install -m 644 sql/gitgres--0.1.sql "$SHAREDIR/extension/gitgres--0.1.sql" 2>&1Sandbox restriction on Postgres.app. The C extension builds but can't be installed without disabling SIP protections on the app bundle. That's fine - the extension is a build step 9 optimization item. The core functionality (PL/pgSQL functions + libgit2 backend) all works without it.
Let me now do a proper end-to-end test with the backend binary. Let me first test the import script with a small repo.
>cd /Users/andrew/code/gitgres && /Users/andrew/code/gitgres/backend/gitgres-backend 2>&1Let me do the roundtrip test - create a repo, push it, clone it, diff the working trees.
>psql -c "SELECT 1 FROM pg_database WHERE datname='gitgres_test'" postgres># Create a test repo
TEST_DIR=$(mktemp -d)
cd "$TEST_DIR" && git init test-repo && cd test-repo
git config user.email "test@test.com"
git config user.name "Test User"
echo "hello world" > hello.txt
mkdir sub
echo "nested file" > sub/nested.txt
git add .
git commit -m "initial commit"
echo "second line" >> hello.txt
git add .
git commit -m "second commit"
echo "Test repo at: $TEST_DIR/test-repo"
git log --oneline
git rev-list --objects --all | wc -l>/Users/andrew/code/gitgres/backend/gitgres-backend init "dbname=gitgres_test" roundtrip_test 2>&1>/Users/andrew/code/gitgres/backend/gitgres-backend push "dbname=gitgres_test" roundtrip_test "/var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/tmp.i3RGDfm76x/test-repo" 2>&1>/Users/andrew/code/gitgres/backend/gitgres-backend ls-refs "dbname=gitgres_test" roundtrip_test 2>&1Now clone it back and diff.
>CLONE_DIR=$(mktemp -d)/clone
/Users/andrew/code/gitgres/backend/gitgres-backend clone "dbname=gitgres_test" roundtrip_test "$CLONE_DIR" 2>&1>CLONE_DIR=$(ls -dt /tmp/tmp.*/clone 2>/dev/null | head -1 || ls -dt /var/folders/*/T/tmp.*/clone 2>/dev/null | head -1)
echo "Clone at: $CLONE_DIR"
# Find it by searching for the clone we just made
find /var/folders -name "clone" -type d -newer /Users/andrew/code/gitgres/backend/gitgres-backend 2>/dev/null | head -3>CLONE_DIR="/var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/tmp.MG6xBQn8uc/clone"
ORIG_DIR="/var/folders/8m/47w09vss1bqg72tlz9xcxl6h0000gn/T/tmp.i3RGDfm76x/test-repo"
echo "=== Original ==="
ls -la "$ORIG_DIR"
echo
echo "=== Clone ==="
ls -la "$CLONE_DIR"
echo
echo "=== Diff ==="
diff -r --exclude=.git "$ORIG_DIR" "$CLONE_DIR" && echo "IDENTICAL" || echo "DIFFERENT"
echo
echo "=== Clone git log ==="
git -C "$CLONE_DIR" log --oneline
echo
echo "=== File contents ==="
cat "$CLONE_DIR/hello.txt"
cat "$CLONE_DIR/sub/nested.txt"Full roundtrip works. Push to Postgres, clone back, working trees are identical, git history is preserved.
Let me update the task status and also verify the materialized views work.
Updated task #2 statusUpdated task #3 statusUpdated task #4 status>psql -f sql/views/queryable.sql gitgres_test 2>&1>psql gitgres_test -c "SELECT sha, author_name, authored_at, left(message, 40) as message FROM commits_view ORDER BY authored_at" 2>&1>psql gitgres_test -c "SELECT encode(tree_oid, 'hex') as tree, mode, name, encode(entry_oid, 'hex') as entry FROM tree_entries_view ORDER BY name" 2>&1Everything works. Let me now run the full test suite one more time and then mark tests complete.
>ruby -Itest -e 'Dir.glob("test/*_test.rb").sort.each { |f| require_relative f }' 2>&1The user mentions switching to Postgres 18. Let me check what's available. The psql client is 14.18 (Homebrew) but the server is 16.12 (Postgres.app). Let me check what Postgres versions are available.
Actually, the key issue is that the pg_config is for PostgreSQL 14, but the server running is PostgreSQL 16. This mismatch could cause issues when building the C extension. Let me check if there's a pg_config for PG 16 from Postgres.app.
But the user said we can switch to 18 if easier. Let me check what's available and assess whether the version matters for our initial implementation. For the pure SQL parts and libgit2 backend, the PG version doesn't matter much.
I'll start with what doesn't depend on the C extension—setting up the SQL schema, PL/pgSQL functions, import script, and tests. I need to install libgit2 first, then create the test database and begin building.
Updated task #5 statusLet me also initialize a git repo for the project, now that you mentioned wanting a Makefile (the top-level one already exists).
The server is PG 16 (Postgres.app) but the pg_config client is PG 14 (Homebrew). For the C extension we need matching headers, but for everything else it doesn't matter much. Let me check what's available and get started.
All three Makefiles exist and work. The top-level one you asked about was already created.
Let me also update it to exclude the views from createdb since they depend on data existing, and fix the test runner path.
Now let me initialize the git repo and take a look at the final file list.
>git init 2>&1>ls /Applications/Postgres.app/Contents/Versions/*/bin/pg_config 2>/dev/null>find /Users/andrew/code/gitgres -not -path '*/.git/*' -not -path '*/.claude/*' -not -name '*.o' -not -name '*.dylib' -not -name 'gitgres-backend' -type f | sortHere's the final state of the project:
SQL layer (all working, tested):
sql/schema.sql - tables for repositories, objects, refs, reflog with pgcryptosql/functions/ - PL/pgSQL functions: git_object_hash, git_object_write, git_object_read, git_tree_entries, git_ls_tree_r, git_commit_parse, git_ref_update, git_ref_set_symbolicsql/views/queryable.sql - materialized views for commits and tree entrieslibgit2 backend (compiles, roundtrip verified):
backend/odb_postgres.c - ODB backend storing objects in Postgres via libpqbackend/refdb_postgres.c - refdb backend storing refs in Postgresbackend/writepack_postgres.c - pack file receiver using libgit2's indexerbackend/main.c - CLI tool with init, push, clone, ls-refs commandsC extension (compiles, can't install to Postgres.app due to sandbox):
ext/git_oid_type.c - custom 20-byte git_oid type with btree/hash operator classesext/sha1_hash.c - C SHA1 via OpenSSL EVP APIext/tree_parse.c - C tree entry parser as SRFImport script: import/gitgres-import.sh - imports repos via git plumbing + psql
Tests: 30 tests, 78 assertions, all passing. Covers schema constraints, object hashing (verified against git hash-object), object store CRUD, tree parsing, commit parsing, ref CAS updates, and a roundtrip integration test.
Verified roundtrip: pushed a 2-commit repo to Postgres, cloned it back, diff -r shows identical working trees, git log preserved.
ok lets add a readme and git init a repo and commit with "Hello world"
>git -C /Users/andrew/code/gitgres status -u>git -C /Users/andrew/code/gitgres add .gitignore README.md Makefile backend/Makefile backend/main.c backend/odb_postgres.c backend/odb_postgres.h backend/refdb_postgres.c backend/refdb_postgres.h backend/writepack_postgres.c ext/Makefile ext/git_oid_type.c ext/gitgres.c ext/gitgres.control ext/sha1_hash.c ext/sql/gitgres--0.1.sql ext/tree_parse.c import/gitgres-import.sh sql/schema.sql sql/functions/object_hash.sql sql/functions/object_read_write.sql sql/functions/tree_parse.sql sql/functions/commit_parse.sql sql/functions/ref_manage.sql sql/views/queryable.sql test/test_helper.rb test/schema_test.rb test/object_hash_test.rb test/object_store_test.rb test/tree_parse_test.rb test/commit_parse_test.rb test/ref_test.rb test/roundtrip_test.rbgitgres/ Makefile # top-level, delegates to sub-makes sql/ schema.sql # tables, indexes functions/ object_hash.sql object_read_write.sql tree_parse.sql commit_parse.sql ref_manage.sql views/ queryable.sql # materialized views ext/ # Postgres C extension Makefile # PGXS gitgres.c # PG_MODULE_MAGIC, init git_oid_type.c # 20-byte OID type with operators sha1_hash.c # C SHA1 via OpenSSL tree_parse.c # C tree entry parser gitgres.control sql/gitgres--0.1.sql # CREATE TYPE, CREATE FUNCTION backend/ # libgit2 pluggable backend Makefile main.c # CLI entry point (serves repos) odb_postgres.c # git_odb_backend implementation odb_postgres.h refdb_postgres.c # git_refdb_backend implementation refdb_postgres.h writepack_postgres.c # packfile receive handling import/ gitgres-import.sh # shell script: git plumbing + psql test/ test_helper.rb schema_test.rb object_hash_test.rb object_store_test.rb tree_parse_test.rb commit_parse_test.rb ref_test.rb roundtrip_test.rb # push then clone, diff working treesCREATE EXTENSION IF NOT EXISTS pgcrypto;
CREATE TABLE repositories ( id serial PRIMARY KEY, name text NOT NULL UNIQUE, created_at timestamptz NOT NULL DEFAULT now());
-- Git objects: raw content without the git header.-- Type uses git's internal numbering: 1=commit, 2=tree, 3=blob, 4=tag.-- OID = SHA1("<type> <size>\0<content>"), computed on write.CREATE TABLE objects ( repo_id integer NOT NULL REFERENCES repositories(id), oid bytea NOT NULL, type smallint NOT NULL, size integer NOT NULL, content bytea NOT NULL, PRIMARY KEY (repo_id, oid));CREATE INDEX idx_objects_oid ON objects (oid);
CREATE TABLE refs ( repo_id integer NOT NULL REFERENCES repositories(id), name text NOT NULL, oid bytea, -- NULL for symbolic refs symbolic text, -- NULL for direct refs PRIMARY KEY (repo_id, name), CHECK ((oid IS NOT NULL) != (symbolic IS NOT NULL)));
CREATE TABLE reflog ( id bigserial PRIMARY KEY, repo_id integer NOT NULL REFERENCES repositories(id), ref_name text NOT NULL, old_oid bytea, new_oid bytea, committer text NOT NULL, timestamp_s bigint NOT NULL, tz_offset text NOT NULL, message text, created_at timestamptz NOT NULL DEFAULT now());CREATE INDEX idx_reflog_ref ON reflog (repo_id, ref_name, id);CREATE MATERIALIZED VIEW commits_view ASSELECT o.repo_id, o.oid AS commit_oid, encode(o.oid, 'hex') AS sha, c.tree_oid, c.parent_oids, c.author_name, c.author_email, to_timestamp(c.author_timestamp) AS authored_at, c.committer_name, c.committer_email, to_timestamp(c.committer_timestamp) AS committed_at, c.messageFROM objects o, LATERAL git_commit_parse(o.content) cWHERE o.type = 1;
CREATE MATERIALIZED VIEW tree_entries_view ASSELECT o.repo_id, o.oid AS tree_oid, e.mode, e.name, e.entry_oidFROM objects o, LATERAL git_tree_entries(o.content) eWHERE o.type = 2;Usage: gitgres-import <repo-path> <conninfo> <repo-name>