Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
gilbitron committed Jun 5, 2017
1 parent c3eb72e commit 4ed7b4b
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/.idea
/vendor/
composer.lock
42 changes: 41 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,41 @@
# laravel-migrate-to-branch
# Laravel migrate:to-branch

This is a Laravel Artisan command to rollback migrations before switching to a given branch.

Imagine the scenario where you are working on a feature branch with some new migrations that have
been run on the database. Now you want to switch back to the develop branch but you need to
rollback the migrations to the state they were on the develop branch. This command makes this process
easier by working out which migrations need rolled back and then running the `migrate:rollback` command
for you.

**Note:** This command needs run _before_ you switch branches.

## Install

Require the library by running:

```
composer require gilbitron/laravel-migrate-to-branch
```

Next you need to add the following to your `providers` array in `config/app.php`:

```
Gilbitron\Laravel\MigrateToBranchServiceProvider::class
```

## Usage

Before switching to a different branch run the following command using the name of the destination branch:

```
php artisan migrate:to-branch {branch}
```

If you want to see which migrations need rolled back without actually running the `migrate:rollback` command
you can use the `--dry-run` flag.

## Credits

Laravel "migrate:to-branch" was created by [Gilbert Pellegrom](https://gilbert.pellegrom.me) from
[Dev7studios](https://dev7studios.co). Released under the MIT license.
20 changes: 20 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "gilbitron/laravel-migrate-to-branch",
"description": "A Laravel Artisan command to rollback migrations before switching to a given branch",
"type": "library",
"license": "MIT",
"authors": [
{
"name": "Gilbert Pellegrom",
"email": "gilbert@pellegrom.me"
}
],
"require": {
"laravel/framework": "5.*"
},
"autoload": {
"psr-4": {
"Gilbitron\\Laravel\\": "src/"
}
}
}
146 changes: 146 additions & 0 deletions src/Console/Commands/MigrateToBranch.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
<?php

namespace Gilbitron\Laravel\Console\Commands;

use Illuminate\Database\Console\Migrations\BaseCommand;
use Illuminate\Support\Collection;

class MigrateToBranch extends BaseCommand
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'migrate:to-branch {branch : The name of the branch}
{--database= : The database connection to use}
{--path= : The path of migrations files to be executed}
{--dry-run : Output the migrations that need rolled back}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Rollback migrations before switching to a given branch';

/**
* The migrator instance.
*
* @var \Illuminate\Database\Migrations\Migrator
*/
protected $migrator;

/**
* Create a new command instance.
*/
public function __construct()
{
parent::__construct();

// For some reason dependency injecting this class doesn't work

This comment has been minimized.

Copy link
@geoffreyvanwyk

geoffreyvanwyk Dec 6, 2017

Because it is a string bound to an instance, instead of an interface bound to an implementation.

$this->migrator = app('migrator');
}

/**
* Execute the console command.
*
* @return mixed
*/
public function handle()
{
$destBranch = $this->argument('branch');
$currentBranch = trim(shell_exec('git rev-parse --abbrev-ref HEAD'));

if ($destBranch == $currentBranch) {
$this->error('Already on branch ' . $destBranch);
return;
}

$rollbackMigrations = $this->getMigrationsToRollBack($currentBranch, $destBranch);
$ranMigrations = $this->getRanMigrations();

$steps = $rollbackMigrations->reject(function ($migration) use ($ranMigrations) {
return !in_array($migration, $ranMigrations->toArray());
});

if ($steps->count()) {
if ($this->option('dry-run')) {
$this->info('Rollback required! ' . $steps->count() . ' migrations need rolled back:');

$steps->each(function ($item) {
$this->line(' - ' . $item);
});

$this->info('Run the following command: php artisan migrate:rollback --step=' . $steps->count());
} else {
$this->call('migrate:rollback', [
'--step' => $steps->count(),
]);
}
} else {
$this->info('No migrations need rolled back');
}
}

/**
* Get migrations to roll back
*
* @param string $currentBranch
* @param string $destBranch
* @return Collection
*/
protected function getMigrationsToRollBack($currentBranch, $destBranch)
{
$command = 'cd ' . base_path() . ' && git diff --name-status ';
$command .= $currentBranch . '..' . $destBranch;
$command .= ' -- database/migrations';

/*
* Format:
* A database/migrations/2017_06_02_105859_example1_migration.php
* D database/migrations/2017_06_02_105859_example2_migration.php
*/
$output = trim(shell_exec($command));
$migrations = explode("\n", $output);

return collect($migrations)->reject(function ($migration) {
// We only need migrations that don't exist in the dest branch
return !starts_with($migration, 'D');
})->map(function ($migration) {
return basename($migration, '.php');
});
}

/**
* Get migrations that have been run
*
* @return Collection
*/
protected function getRanMigrations()
{
$this->migrator->setConnection($this->option('database'));

if (!$this->migrator->repositoryExists()) {
return collect();
}

$ran = $this->migrator->getRepository()->getRan();

return collect($this->getAllMigrationFiles())->reject(function ($migration) use ($ran) {
$migrationName = $this->migrator->getMigrationName($migration);

return !in_array($migrationName, $ran);
})->keys();
}

/**
* Get an array of all of the migration files.
*
* @return array
*/
protected function getAllMigrationFiles()
{
return $this->migrator->getMigrationFiles($this->getMigrationPaths());
}
}
32 changes: 32 additions & 0 deletions src/MigrateToBranchServiceProvider.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace Gilbitron\Laravel;

use Illuminate\Support\ServiceProvider;

class MigrateToBranchServiceProvider extends ServiceProvider
{
protected $commands = [
\Gilbitron\Laravel\Console\Commands\MigrateToBranch::class,
];

/**
* Bootstrap the application services.
*
* @return void
*/
public function boot()
{
//
}

/**
* Register the application services.
*
* @return void
*/
public function register()
{
$this->commands($this->commands);
}
}

0 comments on commit 4ed7b4b

Please sign in to comment.