<!-- mobian-agent-page publisher="dailydev" canonical="https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3" -->

---
title: Migration files and Git do not mix — DiffyPick Blog
description: Migration directories look like ordinary files in a Git repo but don&#x27;t get any of Git&#x27;s real protections: diffs show deltas rather than resulting schema state,...
canonical: https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3
twitter:card: summary_large_image
twitter:site: @dailydotdev
og:type: website
og:site_name: daily.dev
og:title: Migration files and Git do not mix — DiffyPick Blog | daily.dev
og:description: Migration directories look like ordinary files in a Git repo but don&#x27;t get any of Git&#x27;s real protections: diffs show deltas rather than resulting schema state,...
og:url: https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3
og:image: https://api.daily.dev/og/posts/IOp2y0qF3.png
og:image:alt: Migration files and Git do not mix — DiffyPick Blog
og:image:width: 1200
og:image:height: 630
og:locale: en
---

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

# Migration files and Git do not mix — DiffyPick Blog

**[Planet PostgreSQL](https://daily.dev/sources/planet-postgresql)** · 7 min read · 28 upvotes · 11 comments

## Summary

Migration directories look like ordinary files in a Git repo but don't get any of Git's real protections: diffs show deltas rather than resulting schema state, concurrent conflicting migrations merge cleanly because they live in separate files, merge order can disagree with apply order, git revert only deletes a file without undoing the database change, and checkout doesn't roll back local database state. Declarative schema files (schema.rb, schema.prisma) cooperate with Git because they're mutable and stateful, which is why Rails and Prisma layer a declarative snapshot on top of migrations — though that snapshot only reflects intended state, not actual drift between databases. The argument concludes that real safety for schema changes has to come from comparing live databases directly, not from Git.

## Full article

daily.dev links to this article rather than hosting it. Read it at the original source: <https://postgr.es/p/9tE>

## Questions this post answers

### why do two conflicting database migrations merge cleanly in git without a conflict

Because each developer's migration lives in its own separate, timestamped file, so git never sees the same lines being touched and has nothing to flag as conflicting. Both migrations merge cleanly and both apply, even when they make semantically incompatible schema changes to the same table, since the migration file format guarantees concurrent changes never land in the same file.

_Anyone debugging a schema clash after a clean merge can compare database states directly, a workflow worth tracking on daily.dev._

### does git revert undo a database migration

No, running git revert on a migration commit only deletes the migration file from the repository; it does not undo the schema change in the database or remove the entry from the migration history table. The database keeps the applied change while the repo now claims the migration doesn't exist, creating a new inconsistency rather than resolving one.

_Teams relying on migrations for rollback safety often follow schema and database tooling discussions on daily.dev._

### how does django handle migration merge conflicts differently from other migration tools

Django models migration dependencies as an explicit directed acyclic graph and forces divergent branches to be resolved with an explicit merge migration, rather than relying on timestamp ordering alone. This narrows the problem where merge order in the repo disagrees with the order migrations were actually applied across environments, a failure mode most timestamp-ordered migration tools have no equivalent protection against.

_Developers comparing migration tooling across frameworks can keep up with these design tradeoffs on daily.dev._

## Community discussion

Top comments from developers on daily.dev.

**@pdfopsdev** · 2 upvotes

> had this bite us with a rails migration revert. we deleted the file but schema_migrations still had the version marked as applied, so two envs silently diverged. tools like atlas or sqlc's diff step catch that class of drift because they compare live state, not migration history.

**@aliceinlimbo** · 2 upvotes

> the article feels all over the place to me...git was never meant to handle database states; its there to handle & version textual changes between files. that there are external services or systems was never part of the design. which is why you need a separate migratino tool to track during runtime the state of the database & what changed. Alembic can be one such tool to check drifts etc.

**@gaburayondev** · 2 upvotes

> The "event log wearing a file system costume" framing is the sharpest line in this — it explains exactly why every Git operation degrades once it touches a migration directory: Git's protections assume mutable, comparable state, and migrations are explicitly append-only by convention. The `git revert` point is the one that trips people up in practice — deleting the migration file does nothing to the database that already ran it, so revert silently lies about what it accomplished. Worth flagging this is also the vendor's own pitch for their db-diffing tool, so the "compare live databases"...

**@rix67** · 0 upvotes

> Good point. Git has no idea what happened to the database.

**@fmenegossi** · 0 upvotes

> Yeah well, git will also not tell if the line you checked-in compiles during the CI pipeline or if the side effects of a very well tested new feature will be cataclysmic when running against the DB of that very specific customer you have. Git does version control for files, period. Migrations have only 1 version, another period.
>
> Wanna know the side-effects and make sure your baby behaves well? Write tests! As many and as good as you can. Tests are like money: it won't buy you peace, but they get you very close to that.

---

Tags: [#database](https://daily.dev/tags/database), [#postgresql](https://daily.dev/tags/postgresql), [#git](https://daily.dev/tags/git)

[View this post on daily.dev](https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3)

```json
{"@context":"https://schema.org","@graph":[{"@type":"Organization","@id":"https://daily.dev/#organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180},"sameAs":["https://twitter.com/dailydotdev","https://github.com/dailydotdev","https://www.linkedin.com/company/daily-dev-ltd"]},{"@type":"WebSite","@id":"https://daily.dev/#website","url":"https://daily.dev","name":"daily.dev","publisher":{"@id":"https://daily.dev/#organization"},"potentialAction":{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https://daily.dev/search?q={search_term_string}"},"query-input":"required name=search_term_string"}}]}
{"@context":"https://schema.org","@type":"TechArticle","headline":"Migration files and Git do not mix — DiffyPick Blog","url":"https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3","mainEntityOfPage":{"@type":"WebPage","@id":"https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3"},"datePublished":"2026-09-01T16:31:20.410Z","dateModified":"2026-09-14T09:15:02.434Z","description":"Migration directories look like ordinary files in a Git repo but don't get any of Git's real protections: diffs show deltas rather than resulting schema state,...","image":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/c2cc8e15868c974351e579fa1c8e1dec?_a=AQAEuop","thumbnailUrl":"https://media.daily.dev/image/upload/f_auto,q_auto/v1/posts/c2cc8e15868c974351e579fa1c8e1dec?_a=AQAEuop","isAccessibleForFree":true,"articleSection":"Planet PostgreSQL","inLanguage":"en","publisher":{"@type":"Organization","name":"daily.dev","url":"https://daily.dev","logo":{"@type":"ImageObject","url":"https://daily.dev/apple-touch-icon.png","width":180,"height":180}},"author":{"@type":"Organization","name":"Planet PostgreSQL","logo":"https://media.daily.dev/image/upload/logos/placeholder.jpg","url":"https://daily.dev/sources/planet-postgresql"},"commentCount":11,"discussionUrl":"https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3","interactionStatistic":[{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":28},{"@type":"InteractionCounter","interactionType":{"@type":"CommentAction"},"userInteractionCount":11}],"keywords":"database,postgresql,git","timeRequired":"PT7M"}
{"@context":"https://schema.org","@type":"BreadcrumbList","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https://daily.dev"},{"@type":"ListItem","position":2,"name":"Planet PostgreSQL","item":"https://daily.dev/sources/planet-postgresql"},{"@type":"ListItem","position":3,"name":"Migration files and Git do not mix — DiffyPick Blog"}]}
{"@context":"https://schema.org","@type":"WebPage","@id":"https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3","comment":[{"@type":"Comment","text":"had this bite us with a rails migration revert. we deleted the file but schema_migrations still had the version marked as applied, so two envs silently diverged. tools like atlas or sqlc’s diff step catch that class of drift because they compare live state, not migration history.","datePublished":"2026-09-01T18:03:11.988Z","url":"https://daily.dev/posts/IOp2y0qF3#c-YiNPdFdOO","author":{"@type":"Person","name":"PDFops","url":"https://daily.dev/pdfopsdev","image":"https://media.daily.dev/image/upload/s---8isRBKc--/f_auto/v1782922291/avatars/avatar_orjMeK8QKaaVZwGq7ScPz?_a=BAMAMicg0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2}},{"@type":"Comment","text":"the article feels all over the place to me…git was never meant to handle database states; its there to handle &amp; version textual changes between files. that there are external services or systems was never part of the design. which is why you need a separate migratino tool to track during runtime the state of the database &amp; what changed. Alembic can be one such tool to check drifts etc.","datePublished":"2026-09-01T20:11:23.588Z","url":"https://daily.dev/posts/IOp2y0qF3#c-e3ec1BsxC","author":{"@type":"Person","name":"Ikram","url":"https://daily.dev/aliceinlimbo","image":"https://media.daily.dev/image/upload/s--FzqkRD6G--/f_auto/v1734873785/avatars/avatar_YORtdHRwRyH5xdHAdrw7H"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2}},{"@type":"Comment","text":"The “event log wearing a file system costume” framing is the sharpest line in this — it explains exactly why every Git operation degrades once it touches a migration directory: Git’s protections assume mutable, comparable state, and migrations are explicitly append-only by convention. The git revert point is the one that trips people up in practice — deleting the migration file does nothing to the database that already ran it, so revert silently lies about what it accomplished. Worth flagging this is also the vendor’s own pitch for their db-diffing tool, so the “compare live databases” conclusion isn’t neutral — but the underlying diagnosis holds regardless of what tool you reach for after it.","datePublished":"2026-09-07T09:09:56.775Z","url":"https://daily.dev/posts/IOp2y0qF3#c-gME5zJ7vL","author":{"@type":"Person","name":"Gabu Rayon Dev","url":"https://daily.dev/gaburayondev","image":"https://media.daily.dev/image/upload/s--5hPC52y_--/f_auto/v1773412842/avatars/avatar_8fkdziGASnSSBES6c4Ml9?_a=BAMAMiiu0"},"interactionStatistic":{"@type":"InteractionCounter","interactionType":{"@type":"LikeAction"},"userInteractionCount":2}},{"@type":"Comment","text":"Good point. Git has no idea what happened to the database.","datePublished":"2026-09-16T07:29:39.354Z","url":"https://daily.dev/posts/IOp2y0qF3#c-hK5H2SGim","author":{"@type":"Person","name":"Risto Tõldsep","url":"https://daily.dev/rix67","image":"https://lh3.googleusercontent.com/a/ACg8ocLDWc6mZn0JwNmXXw6WY0L_HJ6pegzRttooC5VgtXESHSj3MNYx=s96-c"}},{"@type":"Comment","text":"Yeah well, git will also not tell if the line you checked-in compiles during the CI pipeline or if the side effects of a very well tested new feature will be cataclysmic when running against the DB of that very specific customer you have. Git does version control for files, period. Migrations have only 1 version, another period.\nWanna know the side-effects and make sure your baby behaves well? Write tests! As many and as good as you can. Tests are like money: it won’t buy you peace, but they get you very close to that.","datePublished":"2026-09-07T15:52:19.825Z","url":"https://daily.dev/posts/IOp2y0qF3#c-i6j3W75fe","author":{"@type":"Person","name":"Felipe Menegossi","url":"https://daily.dev/fmenegossi","image":"https://avatars.githubusercontent.com/u/28812491?v=4"}}]}
{"@context":"https://schema.org","@type":"FAQPage","@id":"https://daily.dev/posts/migration-files-and-git-do-not-mix-diffypick-blog-iop2y0qf3#faq","mainEntity":[{"@type":"Question","name":"why do two conflicting database migrations merge cleanly in git without a conflict","acceptedAnswer":{"@type":"Answer","text":"Because each developer's migration lives in its own separate, timestamped file, so git never sees the same lines being touched and has nothing to flag as conflicting. Both migrations merge cleanly and both apply, even when they make semantically incompatible schema changes to the same table, since the migration file format guarantees concurrent changes never land in the same file. Anyone debugging a schema clash after a clean merge can compare database states directly, a workflow worth tracking on daily.dev."}},{"@type":"Question","name":"does git revert undo a database migration","acceptedAnswer":{"@type":"Answer","text":"No, running git revert on a migration commit only deletes the migration file from the repository; it does not undo the schema change in the database or remove the entry from the migration history table. The database keeps the applied change while the repo now claims the migration doesn't exist, creating a new inconsistency rather than resolving one. Teams relying on migrations for rollback safety often follow schema and database tooling discussions on daily.dev."}},{"@type":"Question","name":"how does django handle migration merge conflicts differently from other migration tools","acceptedAnswer":{"@type":"Answer","text":"Django models migration dependencies as an explicit directed acyclic graph and forces divergent branches to be resolved with an explicit merge migration, rather than relying on timestamp ordering alone. This narrows the problem where merge order in the repo disagrees with the order migrations were actually applied across environments, a failure mode most timestamp-ordered migration tools have no equivalent protection against. Developers comparing migration tooling across frameworks can keep up with these design tradeoffs on daily.dev."}}]}
```

