Last active
June 11, 2025 16:36
-
-
Save anthonyjoeseph/102c0e3ea8496fe111029a8b8a95cc3a to your computer and use it in GitHub Desktop.
undo-drizzle-migration-during-merge.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// MIT licensed (© 2024) | |
// https://opensource.org/license/mit | |
// | |
// Source: https://gist.github.com/anthonyjoeseph/102c0e3ea8496fe111029a8b8a95cc3a | |
// motivation: | |
// `drizzle-kit generate` has a default behavior that causes headaches | |
// it will randomly generate names for migration files | |
// e.g. "0012_swift_rhino.sql" | |
// this means that migration conflicts across branches are easy to miss | |
// e.g. one branch might have a migration called "0012_swift_rhino.sql" | |
// while another branch might have "0012_cold_inertia.sql" | |
// while the snapshots & _journal.json will have conflicts, the sql files won't | |
// and it's not immediately obvious how to resolve any of this | |
// usage: | |
// you should use the `--name` flag with drizzle-kit generate | |
// like so: `drizzle-kit generate --name migration` | |
// this will generate migrations that always look like this: "0023_migration.sql" | |
// forcing merge conflicts across branches | |
// | |
// in case of a merge conflict, | |
// use this script to undo your own migration | |
// | |
// it will run `git checkout --theirs ${path.join(config.out)}` | |
// and `git add ${path.join(config.out)}` | |
// and then delete all of the migrations and snapshots | |
// that don't match the incoming branch | |
import { execSync } from "child_process"; | |
import fs from "fs"; | |
import path from "path"; | |
import config from "../../drizzle.config"; | |
const gitRoot = execSync("git rev-parse --show-toplevel").toString().trim(); | |
if (!fs.existsSync(path.join(gitRoot, ".git", "MERGE_HEAD"))) { | |
console.log("no 'git merge' in process"); | |
process.exit(0); | |
} | |
if (!config.out || !fs.existsSync(config.out)) { | |
console.error(`Directory ${config.out} does not exist`); | |
console.error("Maybe your drizzle.config.ts can't be found."); | |
process.exit(1); | |
} | |
const files = fs.readdirSync(config.out).sort(); | |
const snapshotFiles = fs.readdirSync(path.join(config.out, "meta")).sort(); | |
console.log("Resetting migration files to the state of the current branch."); | |
execSync(`git checkout --theirs ${path.join(config.out)}`); | |
execSync(`git add ${path.join(config.out)}`); | |
// read the journal to identify which migrations to keep | |
const journal = JSON.parse( | |
fs.readFileSync(path.join(config.out, "meta", "_journal.json"), "utf8"), | |
) as { | |
entries: { idx: number; tag: string }[]; | |
}; | |
const sortedIdxs = journal.entries.sort(({ idx }, { idx: idx2 }) => idx - idx2); | |
const highestIdx = sortedIdxs[sortedIdxs.length - 1]?.idx; | |
if (highestIdx === undefined) { | |
console.error("malformed _journal.json"); | |
process.exit(1); | |
} | |
console.log(`highest idx in _journal.json: ${highestIdx}`); | |
const unusedMigrations = files.filter((filename) => { | |
if (!filename.endsWith(".sql")) return false; | |
const [idx] = filename.split("_"); | |
return !idx || parseInt(idx) > highestIdx; | |
}); | |
const unusedSnapshots = snapshotFiles.filter((filename) => { | |
if (filename === "_journal.json") return false; | |
const [idx] = filename.split("_"); | |
return !idx || parseInt(idx) > highestIdx; | |
}); | |
for (const unusedMigration of unusedMigrations) { | |
console.log(`deleting unused migration ${unusedMigration}`); | |
fs.unlinkSync(path.join(config.out, unusedMigration)); | |
} | |
for (const unusedSnapshot of unusedSnapshots) { | |
console.log(`deleting unused snapshot ${unusedSnapshot}`); | |
fs.unlinkSync(path.join(config.out, "meta", unusedSnapshot)); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment