-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* add merge command * print "Done!" * clippy * Update src/migrations/merge.rs Co-authored-by: Aljaž Mur Eržen <aljazerzen@users.noreply.github.com> * update error messages to be more clear * change up error message * remove redundant id updates --------- Co-authored-by: Aljaž Mur Eržen <aljazerzen@users.noreply.github.com>
- Loading branch information
1 parent
1d33f4e
commit 7af21d2
Showing
7 changed files
with
220 additions
and
16 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use crate::branch::connections::connect_if_branch_exists; | ||
use crate::branch::context::Context; | ||
use crate::branch::option::{Merge}; | ||
use crate::connect::Connection; | ||
use crate::migrations; | ||
use crate::migrations::merge::{apply_merge_migration_files, get_merge_migrations, write_merge_migrations}; | ||
use crate::options::Options; | ||
|
||
pub async fn main(options: &Merge, context: &Context, source_connection: &mut Connection, cli_opts: &Options) -> anyhow::Result<()> { | ||
if options.target_branch == context.branch { | ||
anyhow::bail!("Cannot merge the current branch into its self"); | ||
} | ||
|
||
let mut target_connection = match connect_if_branch_exists( | ||
cli_opts.create_connector().await?.database(&options.target_branch)? | ||
).await? { | ||
Some(connection) => connection, | ||
None => anyhow::bail!("The branch '{}' doesn't exist", options.target_branch) | ||
}; | ||
|
||
let migration_context = migrations::Context::for_project(&context.project_config)?; | ||
let mut merge_migrations = get_merge_migrations(source_connection, &mut target_connection).await?; | ||
|
||
eprintln!("Merging {} migration(s) into '{}'...", merge_migrations.target_migrations.len(), source_connection.database()); | ||
|
||
write_merge_migrations(&migration_context, &mut merge_migrations).await?; | ||
|
||
if !options.no_apply { | ||
eprintln!("Applying migrations..."); | ||
apply_merge_migration_files(&merge_migrations, &migration_context, source_connection).await?; | ||
} | ||
|
||
eprintln!("Done!"); | ||
|
||
Ok(()) | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,5 +9,6 @@ pub mod option; | |
mod rename; | ||
mod switch; | ||
mod wipe; | ||
mod merge; | ||
|
||
pub use main::branch_main; |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
use indexmap::IndexMap; | ||
use fs_err as fs; | ||
|
||
use crate::connect::Connection; | ||
use crate::migrations::{Context, migrate, migration}; | ||
use crate::migrations::create::{MigrationKey, MigrationToText, write_migration}; | ||
use crate::migrations::db_migration::{DBMigration, read_all}; | ||
use crate::migrations::migration::MigrationFile; | ||
use crate::migrations::rebase::{fix_migration_ids}; | ||
use crate::print; | ||
|
||
pub struct MergeMigrations { | ||
pub base_migrations: IndexMap<String, MergeMigration>, | ||
pub target_migrations: IndexMap<String, MergeMigration> | ||
} | ||
|
||
impl MergeMigrations { | ||
fn flatten(&self) -> IndexMap<&String, &MergeMigration> { | ||
let mut result = IndexMap::new(); | ||
|
||
for migration in self.base_migrations.iter() { | ||
result.insert(migration.0, migration.1); | ||
} | ||
|
||
for migration in self.target_migrations.iter() { | ||
result.insert(migration.0, migration.1); | ||
} | ||
|
||
result | ||
} | ||
} | ||
|
||
pub struct MergeMigration { | ||
key: MigrationKey, | ||
migration: DBMigration | ||
} | ||
|
||
impl MigrationToText for MergeMigration { | ||
type StatementsIter<'a> = std::iter::Once<&'a String> where Self: 'a; | ||
|
||
fn key(&self) -> &MigrationKey { | ||
&self.key | ||
} | ||
|
||
fn parent(&self) -> anyhow::Result<&str> { | ||
Ok(self.migration.parent_names.first().map(String::as_str).unwrap_or("initial")) | ||
} | ||
|
||
fn id(&self) -> anyhow::Result<&str> { | ||
Ok(self.migration.name.as_str()) | ||
} | ||
|
||
fn statements<'a>(&'a self) -> Self::StatementsIter<'a> { | ||
std::iter::once(&self.migration.script) | ||
} | ||
} | ||
|
||
pub async fn get_merge_migrations(base: &mut Connection, target: &mut Connection) -> anyhow::Result<MergeMigrations> { | ||
let base_migrations = read_all(base, true, false).await?; | ||
let mut target_migrations = read_all(target, true, false).await?; | ||
|
||
// check if the base branch is up-to-date with target: | ||
// we do this by verifying that all target migrations | ||
// exist within the base migrations | ||
if base_migrations.len() >= target_migrations.len() && | ||
target_migrations.iter().enumerate() | ||
.all(|(i, (k, _))| | ||
base_migrations.get_index(i) | ||
.map(|v| v.0 == k).unwrap_or(false) | ||
) | ||
{ | ||
anyhow::bail!("Already up to date.") | ||
} | ||
|
||
let mut diverging_migrations = Vec::new(); | ||
|
||
for (index, (base_migration_id, _)) in base_migrations.iter().enumerate() { | ||
if let Some((target_migration_id, _)) = target_migrations.get_index(index) { | ||
if target_migration_id != base_migration_id { | ||
diverging_migrations.push((index, base_migration_id, target_migration_id)) | ||
} | ||
} | ||
} | ||
|
||
if diverging_migrations.len() > 0 { | ||
eprintln!("\nThe migration history of {} diverges from {}:", target.database(), base.database()); | ||
|
||
for (index, expecting, actual) in &diverging_migrations { | ||
eprintln!("{}. Expecting {} but has {}", index, &expecting[..7], &actual[..7]) | ||
} | ||
|
||
eprintln!(); | ||
|
||
anyhow::bail!("Cannot complete fast-forward merge, the histories of {0} and {1} are incompatible. Try rebasing {1} onto {0}", base.database(), target.database()) | ||
} | ||
|
||
let mut target_merge_migrations: IndexMap<String, MergeMigration> = IndexMap::new(); | ||
let mut base_merge_migrations: IndexMap<String, MergeMigration> = IndexMap::new(); | ||
|
||
for (index, (id, migration)) in target_migrations.split_off(base_migrations.len()).into_iter().enumerate() { | ||
target_merge_migrations.insert(id, MergeMigration { | ||
migration, | ||
key: MigrationKey::Index((base_migrations.len() + index + 1) as u64), | ||
}); | ||
} | ||
|
||
for(index, (id, migration)) in target_migrations.into_iter().enumerate() { | ||
base_merge_migrations.insert(id, MergeMigration { | ||
migration, | ||
key: MigrationKey::Index((index + 1) as u64), | ||
}); | ||
} | ||
|
||
Ok(MergeMigrations { | ||
target_migrations: target_merge_migrations, | ||
base_migrations: base_merge_migrations | ||
}) | ||
} | ||
|
||
pub async fn write_merge_migrations(context: &Context, migrations: &mut MergeMigrations) -> anyhow::Result<()> { | ||
let temp_dir = tempfile::tempdir()?; | ||
let temp_ctx = Context { | ||
schema_dir: temp_dir.path().to_path_buf(), | ||
edgedb_version: None, | ||
quiet: false, | ||
}; | ||
|
||
for (_, migration) in migrations.flatten() { | ||
write_migration(&temp_ctx, migration, false).await?; | ||
} | ||
|
||
for from in migration::read_names(&temp_ctx).await? { | ||
let to = context | ||
.schema_dir | ||
.join("migrations") | ||
.join(from.file_name().unwrap()); | ||
|
||
fs::copy(from, to)?; | ||
} | ||
|
||
Ok(()) | ||
} | ||
|
||
pub async fn apply_merge_migration_files(merge_migrations: &MergeMigrations, context: &Context, connection: &mut Connection) -> anyhow::Result<()> { | ||
// apply the new migrations | ||
let migrations: IndexMap<String, MigrationFile> = migration::read_all(context, true).await?.into_iter() | ||
.filter(|(id, _)| merge_migrations.target_migrations.contains_key(id)) | ||
.collect(); | ||
|
||
migrate::apply_migrations(connection, &migrations, context, true).await | ||
} |
This file contains 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
This file contains 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