> ## Documentation Index
> Fetch the complete documentation index at: https://developer.upsun.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Why you should replace PostgreSQL with Git for your next project

> Explore how Git's internal architecture makes it a surprisingly capable database alternative. Learn Git's data structures through a practical todo app implementation that might make you reconsider your database choices.


export const PostMeta = ({data = {}}) => {
  const {author, date, image} = data;
  const authors = Array.isArray(author) ? author : author ? [author] : [];
  const resolveAuthor = slug => {
    const entry = AUTHOR_MAP[slug] || ({});
    const name = entry.name || slug;
    const github = entry.github || null;
    const linkedin = entry.linkedin || null;
    const url = github ? `https://github.com/${github}` : linkedin || null;
    const avatarUrl = github ? `https://github.com/${github}.png?size=64` : null;
    return {
      name,
      url,
      avatarUrl
    };
  };
  const formattedDate = date ? new Date(date).toLocaleDateString('en-US', {
    year: 'numeric',
    month: 'long',
    day: 'numeric'
  }) : null;
  if (!image && authors.length === 0 && !formattedDate) return null;
  const AUTHOR_MAP = {
    "aaron-collier": {
      "name": "Aaron Collier"
    },
    "aaron-dudenhofer": {
      "name": "Aaron Dudenhofer"
    },
    "aaron-porter": {
      "name": "Aaron Porter"
    },
    "adriaan-odendaal": {
      "name": "Adriaan Odendaal"
    },
    "ajmal": {
      "name": "Ajmal Siddiqui"
    },
    "akalipetis": {
      "name": "Antonis Kalipetis"
    },
    "alexander-varwijk": {
      "name": "Alexander Varwijk"
    },
    "alicia-bevilacqua": {
      "name": "Alicia Bevilacqua"
    },
    "amelie-deguerry": {
      "name": "Amelie Deguerry"
    },
    "anacidre": {
      "name": "Ana Cidre",
      "linkedin": "https://www.linkedin.com/in/ana-cidre"
    },
    "andoni": {
      "name": "Andoni Auzmendi"
    },
    "andrei-taranu": {
      "name": "Andrei (Alex) Taranu",
      "linkedin": "https://www.linkedin.com/in/andrei-alex-taranu/"
    },
    "andrew-baxter": {
      "name": "Andrew Baxter"
    },
    "andrew-melck": {
      "name": "Andrew Melck"
    },
    "antoine-crochet-damais": {
      "name": "Antoine Crochet Damais"
    },
    "augustin-delaporte": {
      "name": "Augustin Delaporte",
      "linkedin": "https://www.linkedin.com/in/augustindelaporte/"
    },
    "branislav-bujisic": {
      "name": "Branislav Bujisic"
    },
    "carl-smith": {
      "name": "Carl Smith"
    },
    "caroline-leroy": {
      "name": "Caroline Leroy"
    },
    "cati-mayer": {
      "name": "Cati Mayer"
    },
    "catplat": {
      "name": "C Trinkwon"
    },
    "ceelolulu": {
      "name": "Celeste van der Watt"
    },
    "chadwcarlson": {
      "name": "Chad Carlson",
      "github": "chadwcarlson",
      "linkedin": "https://www.linkedin.com/in/chadwcarlson"
    },
    "chris-ward": {
      "name": "Chris Ward"
    },
    "chris-yates": {
      "name": "Chris Yates"
    },
    "christian-sieber": {
      "name": "Christian Sieber"
    },
    "christopher-lockheardt": {
      "name": "Christopher Lockheardt"
    },
    "christopher-skene": {
      "name": "Christopher Skene"
    },
    "chuck-morgan": {
      "name": "Chuck Morgan"
    },
    "corey-dockendorf": {
      "name": "Corey Dockendorf"
    },
    "crell": {
      "name": "Crell"
    },
    "damz": {
      "name": "Damz"
    },
    "dan-morrison": {
      "name": "Dan Morrison"
    },
    "davidbonachera": {
      "name": "David Bonachera",
      "github": "davidbonachera",
      "linkedin": "https://www.linkedin.com/in/davidbonachera"
    },
    "dereliahmet1": {
      "name": "Ahmet Faruk Dereli"
    },
    "devicezero": {
      "name": "Jonas Kröger",
      "github": "devicezero",
      "linkedin": "https://www.linkedin.com/in/jonaskroeger/"
    },
    "doug-goldberg": {
      "name": "Doug Goldberg"
    },
    "duncan-naves": {
      "name": "Duncan Naves",
      "github": "duncannaves",
      "linkedin": "https://www.linkedin.com/in/duncan-naves-a94423aa"
    },
    "erika-bustamante": {
      "name": "Erika Bustamante"
    },
    "fabpot": {
      "name": "Fabien Potencier"
    },
    "flovntp": {
      "name": "Florent Huck",
      "github": "flovntp",
      "linkedin": "https://www.linkedin.com/in/florenthuck"
    },
    "fred-plais": {
      "name": "Fred Plais"
    },
    "gauthier-garnier": {
      "name": "Gauthier Garnier"
    },
    "gilzow": {
      "name": "Paul Gilzow"
    },
    "gmoigneu": {
      "name": "Guillaume Moigneu",
      "github": "gmoigneu",
      "linkedin": "https://www.linkedin.com/in/guillaumemoigneu/"
    },
    "gregqualls": {
      "name": "Greg Qualls"
    },
    "guguss": {
      "name": "Augustin Delaporte"
    },
    "haylee-millar": {
      "name": "Haylee Millar"
    },
    "ivana-kotur": {
      "name": "Ivana Kotur"
    },
    "jackrabbithanna": {
      "name": "Mark Hanna"
    },
    "jared-wright": {
      "name": "Jared Wright",
      "github": "jww-sh",
      "linkedin": "https://www.linkedin.com/in/jaredwaynewright"
    },
    "jessica-orozco": {
      "name": "Jessica Orozco"
    },
    "joey-stanford": {
      "name": "Joey Stanford"
    },
    "john-grubb": {
      "name": "John Grubb"
    },
    "jonas-kruger": {
      "name": "Jonas Kruger"
    },
    "kathryn-frazer": {
      "name": "Kathryn Frazer"
    },
    "kemiojo": {
      "name": "Kemi Elizabeth Ojogbede"
    },
    "kieronsambrook-smith": {
      "name": "Kieronsambrook Smith"
    },
    "laurent-arnoud": {
      "name": "Laurent Arnoud",
      "linkedin": "https://www.linkedin.com/in/laurent-arnoud-861b44121/"
    },
    "letoya-boyne": {
      "name": "Letoya Boyne"
    },
    "lolautruche": {
      "name": "Jérôme Vieilledent"
    },
    "lyly-lepinay": {
      "name": "Lyly Lepinay"
    },
    "manauwar-alam": {
      "name": "Manauwar Alam"
    },
    "marc-antoine-porri": {
      "name": "Marc Antoine Porri"
    },
    "maria-antinkaapo": {
      "name": "Maria Antinkaapo"
    },
    "maria-de-anton": {
      "name": "Maria De Anton"
    },
    "mark-dorison": {
      "name": "Mark Dorison"
    },
    "markus-hausammann": {
      "name": "Markus Hausammann"
    },
    "mary-thomas": {
      "name": "Mary Thomas"
    },
    "mathias-bolt-lesniak": {
      "name": "Mathias Bolt Lesniak"
    },
    "mathieu-strauch": {
      "name": "Mathieu Strauch"
    },
    "matthias-van-woensel": {
      "name": "Matthias Van Woensel",
      "linkedin": "https://www.linkedin.com/in/matthias-van-woensel-267a069"
    },
    "michael-sharp": {
      "name": "Michael Sharp"
    },
    "mupsi": {
      "name": "Marine Gandy"
    },
    "natalie-harper": {
      "name": "Natalie Harper"
    },
    "ngommenginger": {
      "name": "Nicolas Gommenginger",
      "linkedin": "https://www.linkedin.com/in/nicolas-gommenginger"
    },
    "nicholas-bennison": {
      "name": "Nicholas Bennison"
    },
    "nicholas-vahalik": {
      "name": "Nicholas Vahalik"
    },
    "nick-hardiman": {
      "name": "Nick Hardiman"
    },
    "nickanderegg": {
      "name": "Nickanderegg"
    },
    "nicolas-grekas": {
      "name": "Nicolas Grekas",
      "github": "nicolas-grekas",
      "linkedin": "https://www.linkedin.com/in/nicolasgrekas/"
    },
    "niti-malwade": {
      "name": "Niti Malwade"
    },
    "opensocialteam": {
      "name": "Opensocialteam"
    },
    "ori-pekelman": {
      "name": "Ori Pekelman"
    },
    "otavio-santana": {
      "name": "Otavio Santana"
    },
    "palwandi": {
      "name": "Pawan Alwandi",
      "github": "pawpy",
      "linkedin": "https://www.linkedin.com/in/pawanalwandi"
    },
    "patrick-boest": {
      "name": "Patrick Boest"
    },
    "patrick-dawkins": {
      "name": "Patrick Dawkins",
      "github": "pjcdawkins",
      "linkedin": "https://www.linkedin.com/in/patrickdawkins"
    },
    "patrick-klima": {
      "name": "Patrick Klima"
    },
    "pjcdawkins": {
      "name": "Pjcdawkins"
    },
    "prineet-kaurbhurji": {
      "name": "Prineet Kaurbhurji"
    },
    "quentin-sinig": {
      "name": "Quentin Sinig"
    },
    "ralt": {
      "name": "Florian Margaine",
      "github": "ralt",
      "linkedin": "https://www.linkedin.com/in/florian-margaine-43971136"
    },
    "ramanathanramakrishnamurthy": {
      "name": "Ramanathanramakrishnamurthy"
    },
    "remi-lejeune": {
      "name": "Rémi Lejeune"
    },
    "ribel": {
      "name": "Taras Kruts"
    },
    "robert-douglass": {
      "name": "Robert Douglass"
    },
    "rudy-weber": {
      "name": "Rudy Weber"
    },
    "ryan-hicks": {
      "name": "Ryan Hicks"
    },
    "sabri-helal": {
      "name": "Sabri Helal"
    },
    "savannah-bergeron": {
      "name": "Savannah Bergeron"
    },
    "shannon-vettes": {
      "name": "Shannon Vettes"
    },
    "shawn-ogasawara": {
      "name": "Shawn Ogasawara",
      "linkedin": "https://www.linkedin.com/in/shawn-ogasawara-83a9a0/"
    },
    "shawna-spoor": {
      "name": "Shawna Spoor"
    },
    "shedrack-akintayo": {
      "name": "Shedrack Akintayo"
    },
    "simon-ruggier": {
      "name": "Simon Ruggier"
    },
    "sophie-van-der-kindere": {
      "name": "Sophie Van Der Kindere"
    },
    "stefanos-thampis": {
      "name": "Stefanos Thampis"
    },
    "stephen-weinberg": {
      "name": "Stephen Weinberg"
    },
    "sukhman-virk": {
      "name": "Sukhman Virk"
    },
    "sumaira-nazir": {
      "name": "Sumaira Nazir"
    },
    "sumer": {
      "name": "Sümer Cip"
    },
    "syed-raza": {
      "name": "Syed Raza"
    },
    "tamara-bacchia": {
      "name": "Tamara Bacchia"
    },
    "tara-arnold": {
      "name": "Tara Arnold"
    },
    "theosakamg": {
      "name": "Mickael Gaillard",
      "github": "theosakamg"
    },
    "thomasdiluccio": {
      "name": "Thomas di Luccio"
    },
    "tim-anderson": {
      "name": "Tim Anderson"
    },
    "tom-helmer-hansen": {
      "name": "Tom Helmer Hansen"
    },
    "tylermills": {
      "name": "Tyler Mills"
    },
    "upsun": {
      "name": "Upsun"
    },
    "veronika-tolkachova": {
      "name": "Veronika Tolkachova",
      "linkedin": "https://www.linkedin.com/in/veronika-tolkachova-169167a2"
    },
    "vince-parker": {
      "name": "Vince Parker"
    },
    "vinnie-russo": {
      "name": "Vincenzo Russo"
    },
    "vrobert78": {
      "name": "Vincent Robert",
      "github": "vrobert78",
      "linkedin": "https://www.linkedin.com/in/vincent-robert-498a883"
    },
    "yuriy-babenko": {
      "name": "Yuriy Babenko"
    },
    "yuriy-gerasimov": {
      "name": "Yuriy Gerasimov"
    }
  };
  return <div className="post-meta">
      {(authors.length > 0 || formattedDate) && <div className="post-meta-info">
          {authors.length > 0 && <div className="post-meta-authors">
              {authors.map(slug => {
    const {name, url, avatarUrl} = resolveAuthor(slug);
    const inner = <>
                    {avatarUrl && <img src={avatarUrl} alt={name} className="post-meta-avatar" />}
                    <span className="post-meta-author-name">{name}</span>
                  </>;
    return url ? <a key={slug} href={url} target="_blank" rel="noopener noreferrer" className="post-meta-author">
                    {inner}
                  </a> : <span key={slug} className="post-meta-author">{inner}</span>;
  })}
            </div>}
          {authors.length > 0 && formattedDate && <span className="post-meta-separator" aria-hidden="true">·</span>}
          {formattedDate && <span className="post-meta-date">{formattedDate}</span>}
        </div>}
      {image && <img src={image} alt="" className="post-meta-image" aria-hidden="true" />}
    </div>;
};

<PostMeta data={{ author: ["ralt"], date: "2025-09-19T00:00:00+00:00", image: "/images/posts/insights/why-you-should-replace-postgresql-with-git-for-your-next-project/why-you-should-replace-postgresql-with-git-for-your-next-project.webp" }} />

Every developer knows the pain of choosing the right database for their project. PostgreSQL offers robust relational features, but what if there was a database you're already using every day that could handle your data storage needs?

Meet Git – the version control system that's been hiding its database capabilities in plain sight. Before you close this tab thinking we've lost our minds, consider this: Git provides built-in versioning, handles concurrent access, supports atomic transactions (commits), and offers lightning-fast data retrieval. It even comes with its own query language (Git commands) and built-in backup system (distributed repositories).

While this approach isn't suitable for production applications, exploring Git's internal architecture reveals fascinating insights into how modern databases work. Let's build a todo application using Git as our storage layer to understand these core concepts.

## Git's data model: The foundation

Git organizes data using four fundamental types:

* **Blobs**: Raw data storage (equivalent to table rows)
* **Trees**: Hierarchical organization (like directory structures)
* **Commits**: Transaction records with metadata
* **References**: Pointers to specific data states (like table indexes)

This structure makes Git more similar to hierarchical databases like Apache ZooKeeper than traditional relational systems. Let's experiment with these concepts by building our own "database".

## Setting up your Git database

```bash theme={null}
$ cd $(mktemp -d)
$ git init
```

### Working with blobs: Your data records

Blobs store raw data – think of them as individual database records. Unlike traditional databases, blobs are content-addressable, meaning their unique identifier is derived from their content.

Create a blob containing data:

```bash theme={null}
$ echo 'foo bar baz' | git hash-object -w --stdin
1aeaedbf4ee8dccec5bc2b1f1168efef19378ffd
```

Git stores this blob in its object database using the hash as the filename:

```bash theme={null}
$ ls -al .git/objects/1a/eaedbf4ee8dccec5bc2b1f1168efef19378ffd
-r--r--r-- 1 ralt ralt 26 Sep 22 10:38 .git/objects/1a/eaedbf4ee8dccec5bc2b1f1168efef19378ffd
```

The file contains compressed, binary data. Git provides tools to retrieve the original content:

```bash theme={null}
$ git cat-file -p 1aeaedbf4ee8dccec5bc2b1f1168efef19378ffd
foo bar baz
```

### Trees: Organizing your data structure

Trees group related blobs together, similar to how database tables organize related records. Create a tree by specifying which blobs it should contain:

```bash theme={null}
printf '100644 blob <hash>\t<filename>' | git mktree
```

Using our existing blob:

```bash theme={null}
$ printf '100644 blob 1aeaedbf4ee8dccec5bc2b1f1168efef19378ffd\tbody' | git mktree
d9d24a5d3ea8407a90f87b136283358e6ff30a87
```

Examine the tree structure:

```bash theme={null}
$ git cat-file -p d9d24a5d3ea8407a90f87b136283358e6ff30a87
100644 blob 1aeaedbf4ee8dccec5bc2b1f1168efef19378ffd	body
```

The tree now references our blob with a meaningful name.

### Commits: Transaction records with metadata

Commits wrap trees in transactional context, providing metadata about when and why changes occurred:

```bash theme={null}
git commit-tree [-p <parent>] -m 'message' <tree hash>
```

Create our first transaction record:

```bash theme={null}
$ git commit-tree -m 'first commit' d9d24a5d3ea8407a90f87b136283358e6ff30a87
ba4c8e52fb092cdb810c913004c82f6ae5eae4c9
```

Inspect the commit metadata:

```bash theme={null}
$ git cat-file -p ba4c8e52fb092cdb810c913004c82f6ae5eae4c9
tree d9d24a5d3ea8407a90f87b136283358e6ff30a87
author Florian Margaine <xxx@platform.sh> 1758530691 +0200
committer Florian Margaine <xxx@platform.sh> 1758530691 +0200

first commit
```

Commits automatically include comprehensive metadata:

* Tree reference (data snapshot)
* Author and committer information
* Timestamp for audit trails
* Descriptive message

### References: Making data discoverable

Without references, commits become "dangling" and get garbage collected. References act like database indexes, making specific data states discoverable:

```bash theme={null}
$ echo ba4c8e52fb092cdb810c913004c82f6ae5eae4c9 > .git/refs/heads/foo
```

This creates a "branch" reference pointing to our commit. Git uses different reference namespaces (`.git/refs/heads` for branches, `.git/refs/tags` for tags) similar to database schemas.

You can now query your "database":

```bash theme={null}
$ git log foo
commit ba4c8e52fb092cdb810c913004c82f6ae5eae4c9 (foo)
Author: Florian Margaine <xxx@platform.sh>
Date:   Mon Sep 22 10:44:51 2025 +0200

    first commit
```

## Building a todo application with Git

Now let's apply these concepts to build a functional todo application, demonstrating how Git's architecture compares to traditional database operations.

### Defining our data schema

Our todo application needs a simple data model:

* **Task Title**: The task description
* **Task Status**: Current state (todo/done)

Using Git's architecture, we'll store each field as a separate blob and organize them in trees, with commits representing state changes.

### Creating task data

Create blobs for task titles:

```bash theme={null}
$ echo 'I need to buy a car' | git hash-object -w --stdin
17fa051a3dd27c8e759b6eae068400b81e9279de
$ echo 'I need to sell my house' | git hash-object -w --stdin
831f49497c435f1e38765bc99bd015ec44ed436e
```

Create status value blobs:

```bash theme={null}
$ echo 'done' | git hash-object -w --stdin
19f86f493ab110b8dc8279a024880e44203968d8
$ echo 'todo' | git hash-object -w --stdin
258cd5725da9a125878490703e64117560b11872
```

### Organizing data with trees

Create a task record by combining title and status blobs in a tree:

```bash theme={null}
$ printf '100644 blob 17fa051a3dd27c8e759b6eae068400b81e9279de\ttitle
100644 blob 258cd5725da9a125878490703e64117560b11872\tstatus' | git mktree
b235f13ebd645de5c3dc87e2302ee81bfb77c70d
```

Verify the task structure:

```bash theme={null}
$ git cat-file -p b235f13ebd645de5c3dc87e2302ee81bfb77c70d
100644 blob 258cd5725da9a125878490703e64117560b11872	status
100644 blob 17fa051a3dd27c8e759b6eae068400b81e9279de	title
```

### Creating transactions with commits

Commit the task to create a permanent transaction record:

```bash theme={null}
$ git commit-tree -m 'Add first task: buy a car' b235f13ebd645de5c3dc87e2302ee81bfb77c70d
fcfe7e22edfe12170a48fd802580ebcb05e36d6c
```

Create a reference to make the data discoverable:

```bash theme={null}
$ echo fcfe7e22edfe12170a48fd802580ebcb05e36d6c > .git/refs/heads/todo-list
```

### Querying your Git database

View the complete transaction history:

```bash theme={null}
$ git log -p todo-list
commit fcfe7e22edfe12170a48fd802580ebcb05e36d6c (todo-list)
Author: Florian Margaine <xxx@platform.sh>
Date:   Mon Sep 22 11:04:16 2025 +0200

    Add first task: buy a car

diff --git a/status b/status
new file mode 100644
index 0000000..258cd57
--- /dev/null
+++ b/status
@@ -0,0 +1 @@
+todo
diff --git a/title b/title
new file mode 100644
index 0000000..17fa051
--- /dev/null
+++ b/title
@@ -0,0 +1 @@
+I need to buy a car
```

## Why Git makes sense for specific use cases

While this exploration started as a thought experiment, Git offers genuine advantages for certain applications:

* **Built-in audit trails**: Every change includes timestamp and author information
* **Atomic transactions**: Commits ensure data consistency
* **Distributed architecture**: Multiple nodes can sync data changes
* **Content addressing**: Automatic deduplication and integrity checking

## Real-world applications at Upsun

At Upsun, we leverage Git's database-like properties for specific scenarios where its strengths outweigh traditional database benefits. For developer-facing configuration management, Git provides:

* Automatic versioning for all configuration changes
* Distributed synchronization across development environments
* Native integration with existing developer workflows
* Built-in rollback capabilities through commit history

However, Git has significant limitations as a general-purpose database:

* Limited concurrent access (worse than SQLite)
* No complex query capabilities
* Poor performance with large datasets
* No built-in indexing for non-content searches

## Start building with proper databases on Upsun

While Git makes an interesting database alternative for specific use cases, your production applications deserve better. Upsun provides managed [PostgreSQL, MySQL, and other database services](https://docs.upsun.com/add-services.html) with:

* Automatic scaling and performance optimization
* Built-in backup and disaster recovery
* Multi-environment support for development and staging

[Create a free Upsun account](https://upsun.com/) to deploy your applications with proper database infrastructure that scales with your needs.
