Commits

Mike Bayer committed e80da5e

- [feature] Added support for "relative" migration
identifiers, i.e. "alembic upgrade +2",
"alembic downgrade -1". Courtesy
Atsushi Odagiri for this feature.

Comments (0)

Files changed (4)

   EnvironmentContext.configure(), allowing for the
   configuration of the version table name. #34
 
+- [feature] Added support for "relative" migration
+  identifiers, i.e. "alembic upgrade +2", 
+  "alembic downgrade -1".  Courtesy 
+  Atsushi Odagiri for this feature.
+
 0.3.2
 =====
 - [feature] Basic support for Oracle added, 

alembic/script.py

 _mod_def_re = re.compile(r'(upgrade|downgrade)_([a-z0-9]+)')
 _slug_re = re.compile(r'\w+')
 _default_file_template = "%(rev)s_%(slug)s"
+_relative_destination = re.compile(r'(?:\+|-)\d+')
 
 class ScriptDirectory(object):
     """Provides operations upon an Alembic script directory.
         The iterator yields :class:`.Script` objects.
 
         """
+        if upper is not None and _relative_destination.match(upper):
+            relative = int(upper)
+            revs = list(self._iterate_revisions("head", lower))
+            revs = revs[-relative:]
+            if len(revs) != abs(relative):
+                raise util.CommandError("Relative revision %s didn't "
+                            "produce %d migrations" % (upper, abs(relative)))
+            return iter(revs)
+        elif lower is not None and _relative_destination.match(lower):
+            relative = int(lower)
+            revs = list(self._iterate_revisions(upper, "base"))
+            revs = revs[0:-relative]
+            if len(revs) != abs(relative):
+                raise util.CommandError("Relative revision %s didn't "
+                            "produce %d migrations" % (lower, abs(relative)))
+            return iter(revs)
+        else:
+            return self._iterate_revisions(upper, lower)
+
+    def _iterate_revisions(self, upper, lower):
         lower = self.get_revision(lower)
         upper = self.get_revision(upper)
         script = upper

docs/build/tutorial.rst

 
 We've now added the ``last_transaction_date`` column to the database.
 
+Relative Migration Identifiers
+==============================
+
+As of 0.3.3, relative upgrades/downgrades are also supported.  To move two versions from the current, a decimal value "+N" can be supplied::
+
+    $ alembic upgrade +2
+
+Negative values are accepted for downgrades::
+
+    $ alembic downgrade -1
+
 Getting Information
 ===================
 
 
 Alembic will stop and let you know if more than one version starts with that prefix.
 
+
 Downgrading
 ===========
 
     INFO  [alembic.context] Running upgrade None -> 1975ea83b712
     INFO  [alembic.context] Running upgrade 1975ea83b712 -> ae1027a6acf
 
+
 Auto Generating Migrations
 ===========================
 

tests/test_revision_paths.py

-from tests import clear_staging_env, staging_env, eq_, ne_
+from tests import clear_staging_env, staging_env, eq_, ne_, \
+    assert_raises_message
 from alembic import util
 
 
         ]
     )
 
+def test_relative_upgrade_path():
+    eq_(
+        env._upgrade_revs("+2", a.revision),
+        [
+            (b.module.upgrade, a.revision, b.revision),
+            (c.module.upgrade, b.revision, c.revision),
+        ]
+    )
+
+    eq_(
+        env._upgrade_revs("+1", a.revision),
+        [
+            (b.module.upgrade, a.revision, b.revision),
+        ]
+    )
+
+    eq_(
+        env._upgrade_revs("+3", b.revision),
+        [
+            (c.module.upgrade, b.revision, c.revision),
+            (d.module.upgrade, c.revision, d.revision),
+            (e.module.upgrade, d.revision, e.revision),
+        ]
+    )
+
+def test_invalid_relative_upgrade_path():
+    assert_raises_message(
+        util.CommandError,
+        "Relative revision -2 didn't produce 2 migrations",
+        env._upgrade_revs, "-2", b.revision
+    )
+
+    assert_raises_message(
+        util.CommandError,
+        r"Relative revision \+5 didn't produce 5 migrations",
+        env._upgrade_revs, "+5", b.revision
+    )
+
 def test_downgrade_path():
 
     eq_(
             (a.module.downgrade, a.revision, a.down_revision),
         ]
     )
+
+def test_relative_downgrade_path():
+    eq_(
+        env._downgrade_revs("-1", c.revision),
+        [
+            (c.module.downgrade, c.revision, c.down_revision),
+        ]
+    )
+
+    eq_(
+        env._downgrade_revs("-3", e.revision),
+        [
+            (e.module.downgrade, e.revision, e.down_revision),
+            (d.module.downgrade, d.revision, d.down_revision),
+            (c.module.downgrade, c.revision, c.down_revision),
+        ]
+    )
+
+def test_invalid_relative_downgrade_path():
+    assert_raises_message(
+        util.CommandError,
+        "Relative revision -5 didn't produce 5 migrations",
+        env._downgrade_revs, "-5", b.revision
+    )
+
+    assert_raises_message(
+        util.CommandError,
+        r"Relative revision \+2 didn't produce 2 migrations",
+        env._downgrade_revs, "+2", b.revision
+    )