使用分支#

当两个或多个版本引用同一个父迁移作为其祖先时,分支描述了迁移流中的一个点。当两个不同的源树(都包含在这些源树中独立创建的 Alembic 修订文件)合并到一起时,就会自然发生分支。发生这种情况时,分支的挑战在于将分支合并到一个更改系列中,以便可以将从任一源树单独建立的数据库升级为同样引用合并结果。分支存在的另一种情况是当我们直接创建它们时;在迁移流的某个时刻,我们希望不同的迁移系列独立管理(例如,我们创建一棵树),或者我们希望从根部开始为不同的特性创建单独的迁移流(例如,森林)。我们将说明所有这些情况,从最常见的情况开始,即源合并产生的分支,我们将合并该分支。

从我们在 创建迁移脚本 中开始的“帐户表”示例开始,假设我们有我们的最基本版本 1975ea83b712,它进入第二个修订版 ae1027a6acf,并且这两个修订版的迁移文件已签入我们的源存储库。考虑一下,如果我们将另一个包含针对另一个名为 shopping_cart 的表的修订版的代码分支合并到我们的源存储库中会怎样。此修订版针对我们的第一个 Alembic 修订版进行,该修订版生成了 account。在加载第二个源树后,一个新文件 27c6a30d7c24_add_shopping_cart_table.py 存在于我们的 versions 目录中。它和 ae1027a6acf_add_a_column.py 都引用 1975ea83b712_add_account_table.py 作为“降级”修订版。为了说明

# main source tree:
1975ea83b712 (create account table) -> ae1027a6acf (add a column)

# branched source tree
1975ea83b712 (create account table) -> 27c6a30d7c24 (add shopping cart table)

在上面,我们可以看到 1975ea83b712 是我们的分支点;两个不同的版本都将其引用为其父版本。Alembic 命令 branches 说明了这一事实

$ alembic branches --verbose
Rev: 1975ea83b712 (branchpoint)
Parent: <base>
Branches into: 27c6a30d7c24, ae1027a6acf
Path: foo/versions/1975ea83b712_add_account_table.py

    create account table

    Revision ID: 1975ea83b712
    Revises:
    Create Date: 2014-11-20 13:02:46.257104

             -> 27c6a30d7c24 (head), add shopping cart table
             -> ae1027a6acf (head), add a column

历史也说明了这一点,说明了两个 head 条目以及一个 branchpoint

$ alembic history
1975ea83b712 -> 27c6a30d7c24 (head), add shopping cart table
1975ea83b712 -> ae1027a6acf (head), add a column
<base> -> 1975ea83b712 (branchpoint), create account table

我们可以使用 alembic heads 来查看当前的 heads

$ alembic heads --verbose
Rev: 27c6a30d7c24 (head)
Parent: 1975ea83b712
Path: foo/versions/27c6a30d7c24_add_shopping_cart_table.py

    add shopping cart table

    Revision ID: 27c6a30d7c24
    Revises: 1975ea83b712
    Create Date: 2014-11-20 13:03:11.436407

Rev: ae1027a6acf (head)
Parent: 1975ea83b712
Path: foo/versions/ae1027a6acf_add_a_column.py

    add a column

    Revision ID: ae1027a6acf
    Revises: 1975ea83b712
    Create Date: 2014-11-20 13:02:54.849677

如果我们尝试对 upgrade 运行到 head 的常规最终目标,Alembic 不再认为这是一个明确的命令。由于我们有多个 headupgrade 命令希望我们提供更多信息

$ alembic upgrade head
  FAILED: Multiple head revisions are present for given argument 'head'; please specify a specific
  target revision, '<branchname>@head' to narrow to a specific head, or 'heads' for all heads

upgrade 命令为我们提供了很多选项,我们可以使用这些选项继续进行升级,要么向其提供有关我们希望升级到哪个 head 的信息,要么声明我们希望一次升级 所有 head。但是,在两个源树合并的典型情况下,我们将希望采用第三个选项,即我们可以合并这些分支。

合并分支#

Alembic 合并是一个迁移文件,它将两个或更多“head”文件连接在一起。如果我们现在拥有的两个分支可以说是一个“树”结构,则引入此合并文件将使其变成“菱形”结构

                            -- ae1027a6acf -->
                           /                   \
<base> --> 1975ea83b712 -->                      --> mergepoint
                           \                   /
                            -- 27c6a30d7c24 -->

我们使用 alembic merge 创建合并文件;使用此命令,我们可以向其传递一个参数,例如 heads,这意味着我们希望合并所有 head。或者,我们可以按顺序传递各个修订版本号

$ alembic merge -m "merge ae1 and 27c" ae1027 27c6a
  Generating /path/to/foo/versions/53fffde5ad5_merge_ae1_and_27c.py ... done

查看新文件,我们将其视为常规迁移文件,唯一的新变化是 down_revision 指向两个修订版本

"""merge ae1 and 27c

Revision ID: 53fffde5ad5
Revises: ae1027a6acf, 27c6a30d7c24
Create Date: 2014-11-20 13:31:50.811663

"""

# revision identifiers, used by Alembic.
revision = '53fffde5ad5'
down_revision = ('ae1027a6acf', '27c6a30d7c24')
branch_labels = None

from alembic import op
import sqlalchemy as sa


def upgrade():
    pass


def downgrade():
    pass

此文件是一个常规迁移文件,如果我们愿意,可以将 Operations 指令放入 upgrade()downgrade() 函数中,就像任何其他迁移文件一样。尽管最好将此处放置的指令仅限于处理两个合并分支之间所需的任何类型的协调(如果有)。

heads 命令现在说明,我们 versions/ 目录中的多个 head 已解析到我们的新 head 中

$ alembic heads --verbose
Rev: 53fffde5ad5 (head) (mergepoint)
Merges: ae1027a6acf, 27c6a30d7c24
Path: foo/versions/53fffde5ad5_merge_ae1_and_27c.py

    merge ae1 and 27c

    Revision ID: 53fffde5ad5
    Revises: ae1027a6acf, 27c6a30d7c24
    Create Date: 2014-11-20 13:31:50.811663

历史记录显示了类似的结果,因为合并点变成了我们的 head

$ alembic history
ae1027a6acf, 27c6a30d7c24 -> 53fffde5ad5 (head) (mergepoint), merge ae1 and 27c
1975ea83b712 -> ae1027a6acf, add a column
1975ea83b712 -> 27c6a30d7c24, add shopping cart table
<base> -> 1975ea83b712 (branchpoint), create account table

使用单个 head 目标,可以继续进行常规 upgrade

$ alembic upgrade head
INFO  [alembic.migration] Context impl PostgresqlImpl.
INFO  [alembic.migration] Will assume transactional DDL.
INFO  [alembic.migration] Running upgrade  -> 1975ea83b712, create account table
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> 27c6a30d7c24, add shopping cart table
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> ae1027a6acf, add a column
INFO  [alembic.migration] Running upgrade ae1027a6acf, 27c6a30d7c24 -> 53fffde5ad5, merge ae1 and 27c

使用显式分支#

除了合并外,alembic upgrade 命令还暗示了处理多个头的其他选项。让我们备份并假设我们回到只有 ae1027a6acf27c6a30d7c24 作为我们的头的状态

$ alembic heads
27c6a30d7c24
ae1027a6acf

之前,当我们执行 alembic upgrade head 时,它给出了一个错误,建议 please specify a specific target revision, '<branchname>@head' to narrow to a specific head, or 'heads' for all heads 以便在不合并的情况下继续。我们来介绍一下这些情况。

同时引用所有头#

heads 标识符非常像 head,除了它明确地同时引用所有头。也就是说,它就像告诉 Alembic 同时对 ae1027a6acf27c6a30d7c24 执行操作。如果我们从一个新的数据库开始并运行 upgrade heads,我们会看到

$ alembic upgrade heads
INFO  [alembic.migration] Context impl PostgresqlImpl.
INFO  [alembic.migration] Will assume transactional DDL.
INFO  [alembic.migration] Running upgrade  -> 1975ea83b712, create account table
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> ae1027a6acf, add a column
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> 27c6a30d7c24, add shopping cart table

由于我们已经升级到 heads,并且我们确实有多个头,这意味着这两个不同的头现在都在我们的 alembic_version 表中。如果我们运行 alembic current,我们可以看到这一点

$ alembic current
ae1027a6acf (head)
27c6a30d7c24 (head)

这意味着 alembic_version 中现在有两行。如果我们一次降级一步,Alembic 将从 alembic_version 表中删除每个已关闭的分支,直到只剩下一个分支;然后它将继续将单个值更新到以前的版本

$ alembic downgrade -1
INFO  [alembic.migration] Running downgrade ae1027a6acf -> 1975ea83b712, add a column

$ alembic current
27c6a30d7c24 (head)

$ alembic downgrade -1
INFO  [alembic.migration] Running downgrade 27c6a30d7c24 -> 1975ea83b712, add shopping cart table

$ alembic current
1975ea83b712 (branchpoint)

$ alembic downgrade -1
INFO  [alembic.migration] Running downgrade 1975ea83b712 -> , create account table

$ alembic current

引用特定版本#

我们可以将特定版本号传递给 upgrade。Alembic 将确保调用此版本所依赖的所有修订版本,仅此而已。因此,如果我们 upgrade27c6a30d7c24ae1027a6acf,它将保证 1975ea83b712 已被应用,但不会应用任何“兄弟”版本

$ alembic upgrade 27c6a
INFO  [alembic.migration] Running upgrade  -> 1975ea83b712, create account table
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> 27c6a30d7c24, add shopping cart table

应用 1975ea83b71227c6a30d7c24 后,ae1027a6acf 只是单一附加步骤

$ alembic upgrade ae102
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> ae1027a6acf, add a column

使用分支标签#

为了满足环境具有长期分支(尤其是下一部分将讨论的独立分支)的使用案例,Alembic 支持分支标签的概念。这些是迁移文件中存在的字符串值,使用新的标识符 branch_labels。例如,如果我们想要使用名称“shoppingcart”来引用“购物车”分支,我们可以将该名称添加到我们的文件 27c6a30d7c24_add_shopping_cart_table.py

"""add shopping cart table

"""

# revision identifiers, used by Alembic.
revision = '27c6a30d7c24'
down_revision = '1975ea83b712'
branch_labels = ('shoppingcart',)

# ...

branch_labels 属性引用字符串名称或名称元组,现在将应用于此修订、此修订的所有后代以及此修订的所有祖先(直到前面的分支点,在本例中为 1975ea83b712)。我们可以看到 shoppingcart 标签应用于此修订

$ alembic history
1975ea83b712 -> 27c6a30d7c24 (shoppingcart) (head), add shopping cart table
1975ea83b712 -> ae1027a6acf (head), add a column
<base> -> 1975ea83b712 (branchpoint), create account table

应用标签后,名称 shoppingcart 现在专门用作 27c6a30d7c24 修订的别名。我们可以通过使用 alembic show 来说明这一点

$ alembic show shoppingcart
Rev: 27c6a30d7c24 (head)
Parent: 1975ea83b712
Branch names: shoppingcart
Path: foo/versions/27c6a30d7c24_add_shopping_cart_table.py

    add shopping cart table

    Revision ID: 27c6a30d7c24
    Revises: 1975ea83b712
    Create Date: 2014-11-20 13:03:11.436407

但是,在使用分支标签时,我们通常希望使用称为“分支 at”语法来使用它们;此语法允许我们声明我们想要使用特定修订(例如“头”修订),就特定分支而言。虽然通常情况下,当存在多个头时,我们无法引用 alembic upgrade head,但可以使用 shoppingcart@head 语法专门引用此头

$ alembic upgrade shoppingcart@head
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> 27c6a30d7c24, add shopping cart table

如果我们希望在维护多个分支的同时向我们的版本目录添加新的迁移文件,shoppingcart@head 语法对我们来说变得很重要。就像 upgrade 命令一样,如果我们尝试在没有特定父修订的情况下向我们的多头布局添加新的修订文件,我们将收到一个熟悉的错误

$ alembic revision -m "add a shopping cart column"
  FAILED: Multiple heads are present; please specify the head revision on
  which the new revision should be based, or perform a merge.

alembic revision 命令非常清楚我们需要做什么;为了将我们的新修订专门添加到 shoppingcart 分支,我们使用 --head 参数,可以使用特定修订标识符 27c6a30d7c24,或更通用地使用我们的分支名称 shoppingcart@head

$ alembic revision -m "add a shopping cart column"  --head shoppingcart@head
  Generating /path/to/foo/versions/d747a8a8879_add_a_shopping_cart_column.py ... done

alembic history 现在显示两个文件都是 shoppingcart 分支的一部分

$ alembic history
1975ea83b712 -> ae1027a6acf (head), add a column
27c6a30d7c24 -> d747a8a8879 (shoppingcart) (head), add a shopping cart column
1975ea83b712 -> 27c6a30d7c24 (shoppingcart), add shopping cart table
<base> -> 1975ea83b712 (branchpoint), create account table

我们也可以将历史操作限制在这一分支

$ alembic history -r shoppingcart:
27c6a30d7c24 -> d747a8a8879 (shoppingcart) (head), add a shopping cart column
1975ea83b712 -> 27c6a30d7c24 (shoppingcart), add shopping cart table

如果我们想要说明 shoppingcart 从基础一路走来的路径,我们可以按以下步骤操作

$ alembic history -r :shoppingcart@head
27c6a30d7c24 -> d747a8a8879 (shoppingcart) (head), add a shopping cart column
1975ea83b712 -> 27c6a30d7c24 (shoppingcart), add shopping cart table
<base> -> 1975ea83b712 (branchpoint), create account table

我们也可以从“基础”端运行此操作,但会得到不同的结果

$ alembic history -r shoppingcart@base:
1975ea83b712 -> ae1027a6acf (head), add a column
27c6a30d7c24 -> d747a8a8879 (shoppingcart) (head), add a shopping cart column
1975ea83b712 -> 27c6a30d7c24 (shoppingcart), add shopping cart table
<base> -> 1975ea83b712 (branchpoint), create account table

当我们从 shoppingcart@base 列出而没有端点时,它实际上是 -r shoppingcart@base:heads 的简写,例如所有头,并且由于 shoppingcart@baseae1027a6acf 修订版共享的同一个“基础”,因此我们也会在列表中得到该修订版。当我们处理各个基础时,<branchname>@base 语法非常有用,我们将在下一节中看到。

格式 <branchname>@head 也可以与修订版号一起使用,而不是分支名称,尽管这不太方便。如果我们想在分支中添加一个新修订版,其中包括未标记的 ae1027a6acf,如果这还不是一个头,我们可以按以下方式询问“包含 ae1027a6acf 的分支的头”

$ alembic revision -m "add another account column" --head ae10@head
  Generating /path/to/foo/versions/55af2cb1c267_add_another_account_column.py ... done

更多标签语法#

如果标记的分支本身分解为多个分支,则 heads 符号可以与分支标签组合

$ alembic upgrade shoppingcart@heads

相对迁移标识符 中引入的相对标识符也适用于标签。例如,升级到 shoppingcart@+2 意味着从“shoppingcart”上的当前头开始向上升级两个修订版

$ alembic upgrade shoppingcart@+2

这种操作也可以从历史记录中进行

$ alembic history -r current:shoppingcart@+2

较新的 relnum+delta 格式也可以组合使用,例如,如果我们想要列出 shoppingcart 直到头之前的两个修订版本

$ alembic history -r :shoppingcart@head-2

使用多个基准#

注意

多基准功能旨在允许多个 Alembic 版本化谱系共享同一个 alembic_version 表。这样,谱系内的各个修订版本可以相互交叉依赖。对于一个项目有多个完全独立的修订谱系的情况,这些谱系引用单独的 alembic_version 表,请参阅 从一个 .ini 文件运行多个 Alembic 环境 中的示例。

我们在上一节中看到,如果我们有多个头,alembic upgrade 很好用,alembic revision 允许我们告诉它我们希望将我们的新修订文件与哪个“头”关联,分支标签允许我们为分支分配名称,以便在后续命令中使用。让我们将所有这些放在一起,并引用一个新的“基准”,即一个全新的修订文件树,它将半独立于我们一直在使用的帐户/购物车修订版本。这个新树将处理涉及“网络”的数据库表。

设置多个版本目录#

虽然是可选的,但在使用多个基准时,我们通常希望不同的版本文件集存在于它们自己的目录中;通常,如果应用程序组织成几个子模块,每个子模块都会有一个包含与该模块相关的迁移的版本目录。因此,首先,我们可以编辑 alembic.ini 以引用多个目录;我们还将当前 versions 目录声明为其中之一

# A separator for the location paths must be defined first.
version_path_separator = os  # Use os.pathsep.
# version location specification; this defaults
# to foo/versions.  When using multiple version
# directories, initial revisions must be specified with --version-path
version_locations = %(here)s/model/networking:%(here)s/alembic/versions

新目录 %(here)s/model/networking 是根据 alembic.ini 文件所在的位置来确定的,因为我们正在使用符号 %(here)s,它解析为该位置。当我们针对此目录创建第一个新修订时,如果 model/networking 尚不存在,它将自动创建。一旦我们在此处创建了修订,在生成引用此修订树的后续修订文件时,该路径将自动使用。

创建带标签的基准修订#

我们还希望新分支有自己的名称,为此,我们希望将分支标签应用于基础。为了使用 alembic revision 命令在不编辑的情况下实现这一点,我们需要确保用于生成新修订文件的文件 script.py.mako 具有适当的替换。如果使用 Alembic 0.7.0 或更高版本生成原始迁移环境,则已完成此操作。但是,在使用较旧环境时,需要在 script.py.mako 中添加此指令,通常在 down_revision 指令下方

# revision identifiers, used by Alembic.
revision = ${repr(up_revision)}
down_revision = ${repr(down_revision)}

# add this here in order to use revision with branch_label
branch_labels = ${repr(branch_labels)}

有了这个,我们可以创建一个新的修订文件,启动一个将处理涉及网络的数据库表的分支;我们指定 base--head 版本、networking--branch-label,以及我们希望将此第一个修订文件放在其中的目录,其中 --version-path

$ alembic revision -m "create networking branch" --head=base --branch-label=networking --version-path=model/networking
  Creating directory /path/to/foo/model/networking ... done
  Generating /path/to/foo/model/networking/3cac04ae8714_create_networking_branch.py ... done

如果我们运行了上述命令,但没有较新的 script.py.mako 指令,我们将收到此错误

FAILED: Version 3cac04ae8714 specified branch_labels networking, however
the migration file foo/model/networking/3cac04ae8714_create_networking_branch.py
does not have them; have you upgraded your script.py.mako to include the 'branch_labels'
section?

当我们收到上述错误,并且我们想重试时,我们需要删除生成错误的文件以再次运行 revision我们可以直接编辑 3cac04ae8714_create_networking_branch.py 以添加我们选择的 branch_labels

使用多个基础运行#

一旦我们在系统中有了新的、永久的(只要我们希望它存在)基础,我们就会始终有多个头

$ alembic heads
3cac04ae8714 (networking) (head)
27c6a30d7c24 (shoppingcart) (head)
ae1027a6acf (head)

当我们想要向 networking 添加新的修订文件时,我们将 networking@head 指定为 --head。现在,将根据我们选择的头部自动选择适当的版本目录

$ alembic revision -m "add ip number table" --head=networking@head
  Generating /path/to/foo/model/networking/109ec7d132bf_add_ip_number_table.py ... done

我们必须使用 networking@head 来引用头部;如果我们只引用 networking,则只引用 3cac04ae8714;如果我们指定它,并且它不是头部,alembic revision 将确保我们没有指定头部

$ alembic revision -m "add DNS table" --head=networking
  FAILED: Revision 3cac04ae8714 is not a head revision; please
  specify --splice to create a new branch from this revision

如前所述,由于此基础是独立的,因此我们可以使用 history -r networking@base: 从该基础查看其历史记录

$ alembic history -r networking@base:
109ec7d132bf -> 29f859a13ea (networking) (head), add DNS table
3cac04ae8714 -> 109ec7d132bf (networking), add ip number table
<base> -> 3cac04ae8714 (networking), create networking branch

目前,这是我们在使用 -r :networking@head 时在此处获得的相同输出。但是,在我们使用其他指令后,它稍后会发生变化。

我们现在可以在各个分支之间自由地进行升级或降级(假设数据库再次处于干净状态)

$ alembic upgrade networking@head
INFO  [alembic.migration] Running upgrade  -> 3cac04ae8714, create networking branch
INFO  [alembic.migration] Running upgrade 3cac04ae8714 -> 109ec7d132bf, add ip number table
INFO  [alembic.migration] Running upgrade 109ec7d132bf -> 29f859a13ea, add DNS table

或使用 heads 对整个内容进行升级或降级

$ alembic upgrade heads
INFO  [alembic.migration] Running upgrade  -> 1975ea83b712, create account table
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> 27c6a30d7c24, add shopping cart table
INFO  [alembic.migration] Running upgrade 27c6a30d7c24 -> d747a8a8879, add a shopping cart column
INFO  [alembic.migration] Running upgrade 1975ea83b712 -> ae1027a6acf, add a column
INFO  [alembic.migration] Running upgrade ae1027a6acf -> 55af2cb1c267, add another account column

分支依赖关系#

在使用多个根时,预计这些不同的修订流需要相互引用。例如,networking 中的新修订需要引用 account 表,它将希望将 55af2cb1c267, add another account column(与 account 表一起使用的最后一个修订)建立为依赖关系。从图形的角度来看,这意味着新文件将同时具有 55af2cb1c267, add another account column29f859a13ea, add DNS table 作为“向下”修订,并且看起来就像我们将这两个分支合并在一起一样。但是,我们不想将它们视为“已合并”;我们希望这两个修订流保持独立,即使 networking 中的版本将进入另一个流。为了支持此用例,Alembic 提供了一个称为 depends_on 的指令,该指令允许一个修订文件将另一个文件引用为“依赖关系”,从图形角度来看,这与 down_revision 中的条目非常相似,但从语义角度来看则不同。

要使用 depends_on,我们可以将其指定为 alembic revision 命令的一部分

$ alembic revision -m "add ip account table" --head=networking@head  --depends-on=55af2cb1c267
  Generating /path/to/foo/model/networking/2a95102259be_add_ip_account_table.py ... done

在我们的迁移文件中,我们将看到此新指令

# revision identifiers, used by Alembic.
revision = '2a95102259be'
down_revision = '29f859a13ea'
branch_labels = None
depends_on='55af2cb1c267'

depends_on 可以是实际的修订号或分支名称。在命令行中指定时,解析部分修订号也会起作用。它还可以引用任意数量的依赖修订;例如,如果我们运行命令

$ alembic revision -m "add ip account table" \\
    --head=networking@head  \\
    --depends-on=55af2cb1c267 --depends-on=d747a --depends-on=fa445
  Generating /path/to/foo/model/networking/2a95102259be_add_ip_account_table.py ... done

我们将在文件中看到

# revision identifiers, used by Alembic.
revision = '2a95102259be'
down_revision = '29f859a13ea'
branch_labels = None
depends_on = ('55af2cb1c267', 'd747a8a8879', 'fa4456a9201')

当然,我们也可以在生成文件后手动添加或更改此值,而不是使用 --depends-on 参数。

当我们以“heads”的形式查看 networking 分支的历史记录时,我们可以看到此指令的效果,例如,所有后代修订

$ alembic history -r :networking@head
29f859a13ea (55af2cb1c267) -> 2a95102259be (networking) (head), add ip account table
109ec7d132bf -> 29f859a13ea (networking), add DNS table
3cac04ae8714 -> 109ec7d132bf (networking), add ip number table
<base> -> 3cac04ae8714 (networking), create networking branch
ae1027a6acf -> 55af2cb1c267 (effective head), add another account column
1975ea83b712 -> ae1027a6acf, Add a column
<base> -> 1975ea83b712 (branchpoint), create account table

我们看到的是 networking 分支的完整历史记录,就“升级”到“head”而言,将包括构建 55af2cb1c267, add another account column 的树将首先被拉入。有趣的是,当我们从其他方向显示历史记录时,例如从 networking@base,我们看不到这一点

$ alembic history -r networking@base:
29f859a13ea (55af2cb1c267) -> 2a95102259be (networking) (head), add ip account table
109ec7d132bf -> 29f859a13ea (networking), add DNS table
3cac04ae8714 -> 109ec7d132bf (networking), add ip number table
<base> -> 3cac04ae8714 (networking), create networking branch

出现差异的原因是,从基础显示历史记录向我们展示了如果我们运行降级操作(而不是升级)会发生什么。如果我们使用 networking@base 降级 networking 中的所有文件,则依赖项不会受到影响,它们将保留在原处。

如果我们现在查看 heads,我们还会看到一些奇怪的东西

$ alembic heads
2a95102259be (networking) (head)
27c6a30d7c24 (shoppingcart) (head)
55af2cb1c267 (effective head)

我们用作“依赖项”的头文件 55af2cb1c267 显示为“有效”头,我们也可以在前面的历史记录显示中看到这一点。这意味着目前,如果我们要将所有版本升级到顶部,则 55af2cb1c267 修订号实际上不会出现在 alembic_version 表中;这是因为它没有自己的分支,该分支在依赖于它的 2a95102259be 修订之后

$ alembic upgrade heads
INFO  [alembic.migration] Running upgrade 29f859a13ea, 55af2cb1c267 -> 2a95102259be, add ip account table

$ alembic current
2a95102259be (head)
27c6a30d7c24 (head)

该条目仍显示在 alembic heads 中,因为 Alembic 知道即使此修订不是“真实”头,但它仍然是我们开发人员在语义上认为是头的,因此它会显示出来,并注意其特殊状态,这样当我们在 alembic current 中看不到它时,我们不会那么困惑。

如果我们在 55af2cb1c267 上添加一个新修订,则该分支再次成为一个“真实”分支,它可以在数据库中拥有自己的条目

$ alembic revision -m "more account changes" --head=55af2cb@head
  Generating /path/to/foo/versions/34e094ad6ef1_more_account_changes.py ... done

$ alembic upgrade heads
INFO  [alembic.migration] Running upgrade 55af2cb1c267 -> 34e094ad6ef1, more account changes

$ alembic current
2a95102259be (head)
27c6a30d7c24 (head)
34e094ad6ef1 (head)

为了后代,修订树现在看起来像

$ alembic history
29f859a13ea (55af2cb1c267) -> 2a95102259be (networking) (head), add ip account table
109ec7d132bf -> 29f859a13ea (networking), add DNS table
3cac04ae8714 -> 109ec7d132bf (networking), add ip number table
<base> -> 3cac04ae8714 (networking), create networking branch
1975ea83b712 -> 27c6a30d7c24 (shoppingcart) (head), add shopping cart table
55af2cb1c267 -> 34e094ad6ef1 (head), more account changes
ae1027a6acf -> 55af2cb1c267, add another account column
1975ea83b712 -> ae1027a6acf, Add a column
<base> -> 1975ea83b712 (branchpoint), create account table


                    --- 27c6 --> d747 --> <head>
                   /   (shoppingcart)
<base> --> 1975 -->
                   \
                     --- ae10 --> 55af --> <head>
                                    ^
                                    +--------+ (dependency)
                                             |
                                             |
<base> --> 3782 -----> 109e ----> 29f8 ---> 2a95 --> <head>
         (networking)

如果这里有什么要点需要说明的话,那就是如果你过于随意地进行分支、合并和标记,事情可能会变得非常疯狂!因此,应谨慎、周到地使用分支系统以获得最佳结果。