django-linear-migrations


Namedjango-linear-migrations JSON
Version 2.12.0 PyPI version JSON
download
home_pagehttps://github.com/adamchainz/django-linear-migrations
SummaryEnsure your migrations are linear.
upload_time2023-10-11 09:37:49
maintainer
docs_urlNone
authorAdam Johnson
requires_python>=3.8
licenseMIT
keywords django
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ========================
django-linear-migrations
========================

.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-linear-migrations/main.yml?branch=main&style=for-the-badge
   :target: https://github.com/adamchainz/django-linear-migrations/actions?workflow=CI

.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge
   :target: https://github.com/adamchainz/django-linear-migrations/actions?workflow=CI

.. image:: https://img.shields.io/pypi/v/django-linear-migrations.svg?style=for-the-badge
   :target: https://pypi.org/project/django-linear-migrations/

.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge
   :target: https://github.com/psf/black

.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge
   :target: https://github.com/pre-commit/pre-commit
   :alt: pre-commit

Ensure your migration history is linear.

For a bit of background, see the `introductory blog post <https://adamj.eu/tech/2020/12/10/introducing-django-linear-migrations/>`__.

Requirements
============

Python 3.8 to 3.12 supported.

Django 3.2 to 5.0 supported.

----

**Want to work smarter and faster?**
Check out my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers django-linear-migrations and many other tools to improve your development experience.

----

Installation
============

**First,** install with pip:

.. code-block:: bash

    python -m pip install django-linear-migrations

**Second,** add the app to your ``INSTALLED_APPS`` setting:

.. code-block:: python

    INSTALLED_APPS = [
        ...,
        "django_linear_migrations",
        ...,
    ]

The app relies on overriding the built-in ``makemigrations`` command.
*If your project has a custom* ``makemigrations`` *command,* ensure the app containing your custom command is **above** ``django_linear_migrations``, and that your command subclasses its ``Command`` class:

.. code-block:: python

    # myapp/management/commands/makemigrations.py
    from django_linear_migrations.management.commands.makemigrations import (
        Command as BaseCommand,
    )


    class Command(BaseCommand):
        ...

**Third,** check the automatic detection of first-party apps.
Run this command:

.. code-block:: sh

    python manage.py create_max_migration_files --dry-run

This command is for creating ``max_migration.txt`` files (more on which later) - in dry run mode it lists the apps it would make such files for.
It tries to automatically detect which apps are first-party, i.e. belong to your project.
The automatic detection checks the path of app’s code to see if is within a virtualenv, but this detection can sometimes fail, for example on editable packages installed with ``-e``.
If you see any apps listed that *aren’t* part of your project, define the list of first-party apps’ labels in a ``FIRST_PARTY_APPS`` setting that you combine into ``INSTALLED_APPS``:

.. code-block:: python

    FIRST_PARTY_APPS = []

    INSTALLED_APPS = FIRST_PARTY_APPS + ["django_linear_migrations", ...]

(Note: Django recommends you always list first-party apps first in your project so they can override things in third-party and contrib apps.)

**Fourth,** create the ``max_migration.txt`` files for your first-party apps by re-running the command without the dry run flag:

.. code-block:: sh

    python manage.py create_max_migration_files

In the future, when you add a new app to your project, you’ll need to create its ``max_migration.txt`` file.
Add the new app to ``INSTALLED_APPS`` or ``FIRST_PARTY_APPS`` as appropriate, then rerun the creation command for the new app by specifying its label:

.. code-block:: sh

    python manage.py create_max_migration_files my_new_app

Usage
=====

django-linear-migrations helps you work on Django projects where several branches adding migrations may be in progress at any time.
It enforces that your apps have a *linear* migration history, avoiding merge migrations and the problems they can cause from migrations running in different orders.
It does this by making ``makemigrations`` record the name of the latest migration in per-app ``max_migration.txt`` files.
These files will then cause a merge conflicts in your source control tool (Git, Mercurial, etc.) in the case of migrations being developed in parallel.
The first merged migration for an app will prevent the second from being merged, without addressing the conflict.
The included ``rebase_migration`` command can help automatically such conflicts.

System Checks
-------------

django-linear-migrations comes with several system checks that verify that your ``max_migration.txt`` files are in sync.
These are:

* ``dlm.E001``: ``<app_label>``'s max_migration.txt does not exist.
* ``dlm.E002``: ``<app_label>``'s max_migration.txt contains multiple lines.
* ``dlm.E003``: ``<app_label>``'s max_migration.txt points to non-existent migration '``<bad_migration_name>``'.
* ``dlm.E004``: ``<app_label>``'s max_migration.txt contains '``<max_migration_name>``', but the latest migration is '``<real_max_migration_name>``'.
* ``dlm.E005``: Conflicting migrations detected; multiple leaf nodes in the migration graph: ``<conflicting_migrations>``

``create_max_migration_files`` Command
--------------------------------------

.. code-block:: sh

    python manage.py create_max_migration_files [app_label [app_label ...]]

This management command creates ``max_migration.txt`` files for all first party apps, or the given labels.
It’s used in initial installation of django-linear-migrations, and for recreating.

Pass the ``--dry-run`` flag to only list the ``max_migration.txt`` files that would be created.

Pass the ``--recreate`` flag to re-create files that already exist.
This may be useful after altering migrations with merges or manually.

``rebase_migration`` Command
----------------------------

This management command can help you fix migration conflicts.
Following a conflicted “rebase” operation in Git, run it with the name of the app to auto-fix the migrations for:

.. code-block:: console

    $ python manage.py rebase_migration <app_label>

The command uses the conflict information in the ``max_migration.txt`` file to determine which migration to rebase.
It automatically detects whether a Git merge or rebase operation is in progress, assuming rebase if a Git repository cannot be found.
The command then:

1. renames the migration
2. edits it to depend on the new migration from your main branch
3. updates ``max_migration.txt``.

If Black is installed, the command formats the updated migration file with it, like Django’s built-in migration commands do (from version 4.1+).
See below for some examples and caveats.

Note rebasing the migration might not always be the *correct* thing to do.
If the migrations in your main and feature branches have both affected the same models, rebasing the migration to the end may not make sense.
However, such parallel changes would *normally* cause conflicts in your model files or other parts of the source code as well.

Worked Example
^^^^^^^^^^^^^^

Imagine you were working on your project's ``books`` app in a feature branch called ``titles`` and created a migration called ``0002_longer_titles``.
Meanwhile a commit has been merged to your ``main`` branch with a *different* 2nd migration for ``books`` called ``0002_author_nicknames``.
Thanks to django-linear-migrations, the ``max_migration.txt`` file will show as conflicted between your feature and main branches.

Start the fix by reversing your new migration from your local database.
This is necessary since it will be renamed after rebasing and seen as unapplied.
Do this by switching to the feature branch ``titles`` migrating back to the last common migration:

.. code-block:: console

    $ git switch titles
    $ python manage.py migrate books 0001

Then, fetch the latest code:

.. code-block:: console

    $ git switch main
    $ git pull
    ...

Next, rebase your ``titles`` branch on top of it.
During this process, Git will detect the conflict on ``max_migration.txt``:

.. code-block:: console

    $ git switch titles
    $ git rebase main
    Auto-merging books/models.py
    CONFLICT (content): Merge conflict in books/migrations/max_migration.txt
    error: could not apply 123456789... Increase Book title length
    Resolve all conflicts manually, mark them as resolved with
    "git add/rm <conflicted_files>", then run "git rebase --continue".
    You can instead skip this commit: run "git rebase --skip".
    To abort and get back to the state before "git rebase", run "git rebase --abort".
    Could not apply 123456789... Increase Book title length

The contents of the ``books`` app's ``max_migration.txt`` at this point will look something like this:

.. code-block:: console

    $ cat books/migrations/max_migration.txt
    <<<<<<< HEAD
    0002_author_nicknames
    =======
    0002_longer_titles
    >>>>>>> 123456789 (Increase Book title length)

At this point, use ``rebase_migration`` to automatically fix the ``books`` migration history:

.. code-block:: console

    $ python manage.py rebase_migration books
    Renamed 0002_longer_titles.py to 0003_longer_titles.py, updated its dependencies, and updated max_migration.txt.

This places the conflicted migration on the end of the migration history.
It renames the file appropriately, modifies its ``dependencies = [...]`` declaration, and updates the migration named in ``max_migration.txt`` appropriately.

After this, you should be able to continue the rebase:

.. code-block:: console

    $ git add books/migrations
    $ git rebase --continue

And then migrate your local database to allow you to continue development:

.. code-block:: console

    $ python manage.py migrate books
    Operations to perform:
      Target specific migration: 0003_longer_titles, from books
    Running migrations:
      Applying books.0002_author_nicknames... OK
      Applying books.0003_longer_titles... OK

Code Formatting
^^^^^^^^^^^^^^^

``rebase_migration`` does not guarantee that its edits match your code style.
If you use a formatter like Black, you’ll want to run it after applying ``rebase_migration``.

If you use `pre-commit <https://pre-commit.com/>`__, note that Git does not invoke hooks during rebase commits.
You can run it manually on changed files with ``pre-commit run``.

Branches With Multiple Commits
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Imagine the same example as above, but your feature branch has several commits editing the migration.
This time, before rebasing onto the latest ``main`` branch, squash the commits in your feature branch together.
This way, ``rebase_migration`` can edit the migration file when the conflict occurs.

You can do this with:

.. code-block:: console

    $ git rebase -i --keep-base main

This will open Git’s `interactive mode <https://git-scm.com/docs/git-rebase#_interactive_mode>`__ file.
Edit this so that every comit after the first will be squashed, by starting each line with “s”.
Then close the file, and the rebase will execute.

After this operation, you can rebase onto your latest ``main`` branch as per the previous example.

Branches With Multiple Migrations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``rebase_migration`` does not currently support rebasing multiple migrations (in the same app).
This is `an open feature request <https://github.com/adamchainz/django-linear-migrations/issues/27>`__, but it is not a priority, since it’s generally a good idea to restrict changes to one migration at a time.
Consider merging your migrations into one before rebasing.

Inspiration
===========

I’ve seen similar techniques to the one implemented by django-linear-migrations at several places, and they acted as the inspiration for putting this package together.
My previous client `Pollen <https://pollen.co/>`__ and current client `ev.energy <https://ev.energy/>`__ both have implementations.
This `Doordash blogpost <https://doordash.engineering/2017/05/15/tips-for-building-high-quality-django-apps-at-scale/>`__ covers a similar system that uses a single file for tracking latest migrations.
And there's also a package called `django-migrations-git-conflicts <https://pypi.org/project/django-migrations-git-conflicts/>`__ which works fairly similarly.

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/adamchainz/django-linear-migrations",
    "name": "django-linear-migrations",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "Django",
    "author": "Adam Johnson",
    "author_email": "me@adamj.eu",
    "download_url": "https://files.pythonhosted.org/packages/ea/c0/cf152e4d81ff8814e2738f7d74854412f1d3fcb70b922fc8ca00874686eb/django_linear_migrations-2.12.0.tar.gz",
    "platform": null,
    "description": "========================\ndjango-linear-migrations\n========================\n\n.. image:: https://img.shields.io/github/actions/workflow/status/adamchainz/django-linear-migrations/main.yml?branch=main&style=for-the-badge\n   :target: https://github.com/adamchainz/django-linear-migrations/actions?workflow=CI\n\n.. image:: https://img.shields.io/badge/Coverage-100%25-success?style=for-the-badge\n   :target: https://github.com/adamchainz/django-linear-migrations/actions?workflow=CI\n\n.. image:: https://img.shields.io/pypi/v/django-linear-migrations.svg?style=for-the-badge\n   :target: https://pypi.org/project/django-linear-migrations/\n\n.. image:: https://img.shields.io/badge/code%20style-black-000000.svg?style=for-the-badge\n   :target: https://github.com/psf/black\n\n.. image:: https://img.shields.io/badge/pre--commit-enabled-brightgreen?logo=pre-commit&logoColor=white&style=for-the-badge\n   :target: https://github.com/pre-commit/pre-commit\n   :alt: pre-commit\n\nEnsure your migration history is linear.\n\nFor a bit of background, see the `introductory blog post <https://adamj.eu/tech/2020/12/10/introducing-django-linear-migrations/>`__.\n\nRequirements\n============\n\nPython 3.8 to 3.12 supported.\n\nDjango 3.2 to 5.0 supported.\n\n----\n\n**Want to work smarter and faster?**\nCheck out my book `Boost Your Django DX <https://adamchainz.gumroad.com/l/byddx>`__ which covers django-linear-migrations and many other tools to improve your development experience.\n\n----\n\nInstallation\n============\n\n**First,** install with pip:\n\n.. code-block:: bash\n\n    python -m pip install django-linear-migrations\n\n**Second,** add the app to your ``INSTALLED_APPS`` setting:\n\n.. code-block:: python\n\n    INSTALLED_APPS = [\n        ...,\n        \"django_linear_migrations\",\n        ...,\n    ]\n\nThe app relies on overriding the built-in ``makemigrations`` command.\n*If your project has a custom* ``makemigrations`` *command,* ensure the app containing your custom command is **above** ``django_linear_migrations``, and that your command subclasses its ``Command`` class:\n\n.. code-block:: python\n\n    # myapp/management/commands/makemigrations.py\n    from django_linear_migrations.management.commands.makemigrations import (\n        Command as BaseCommand,\n    )\n\n\n    class Command(BaseCommand):\n        ...\n\n**Third,** check the automatic detection of first-party apps.\nRun this command:\n\n.. code-block:: sh\n\n    python manage.py create_max_migration_files --dry-run\n\nThis command is for creating ``max_migration.txt`` files (more on which later) - in dry run mode it lists the apps it would make such files for.\nIt tries to automatically detect which apps are first-party, i.e. belong to your project.\nThe automatic detection checks the path of app\u2019s code to see if is within a virtualenv, but this detection can sometimes fail, for example on editable packages installed with ``-e``.\nIf you see any apps listed that *aren\u2019t* part of your project, define the list of first-party apps\u2019 labels in a ``FIRST_PARTY_APPS`` setting that you combine into ``INSTALLED_APPS``:\n\n.. code-block:: python\n\n    FIRST_PARTY_APPS = []\n\n    INSTALLED_APPS = FIRST_PARTY_APPS + [\"django_linear_migrations\", ...]\n\n(Note: Django recommends you always list first-party apps first in your project so they can override things in third-party and contrib apps.)\n\n**Fourth,** create the ``max_migration.txt`` files for your first-party apps by re-running the command without the dry run flag:\n\n.. code-block:: sh\n\n    python manage.py create_max_migration_files\n\nIn the future, when you add a new app to your project, you\u2019ll need to create its ``max_migration.txt`` file.\nAdd the new app to ``INSTALLED_APPS`` or ``FIRST_PARTY_APPS`` as appropriate, then rerun the creation command for the new app by specifying its label:\n\n.. code-block:: sh\n\n    python manage.py create_max_migration_files my_new_app\n\nUsage\n=====\n\ndjango-linear-migrations helps you work on Django projects where several branches adding migrations may be in progress at any time.\nIt enforces that your apps have a *linear* migration history, avoiding merge migrations and the problems they can cause from migrations running in different orders.\nIt does this by making ``makemigrations`` record the name of the latest migration in per-app ``max_migration.txt`` files.\nThese files will then cause a merge conflicts in your source control tool (Git, Mercurial, etc.) in the case of migrations being developed in parallel.\nThe first merged migration for an app will prevent the second from being merged, without addressing the conflict.\nThe included ``rebase_migration`` command can help automatically such conflicts.\n\nSystem Checks\n-------------\n\ndjango-linear-migrations comes with several system checks that verify that your ``max_migration.txt`` files are in sync.\nThese are:\n\n* ``dlm.E001``: ``<app_label>``'s max_migration.txt does not exist.\n* ``dlm.E002``: ``<app_label>``'s max_migration.txt contains multiple lines.\n* ``dlm.E003``: ``<app_label>``'s max_migration.txt points to non-existent migration '``<bad_migration_name>``'.\n* ``dlm.E004``: ``<app_label>``'s max_migration.txt contains '``<max_migration_name>``', but the latest migration is '``<real_max_migration_name>``'.\n* ``dlm.E005``: Conflicting migrations detected; multiple leaf nodes in the migration graph: ``<conflicting_migrations>``\n\n``create_max_migration_files`` Command\n--------------------------------------\n\n.. code-block:: sh\n\n    python manage.py create_max_migration_files [app_label [app_label ...]]\n\nThis management command creates ``max_migration.txt`` files for all first party apps, or the given labels.\nIt\u2019s used in initial installation of django-linear-migrations, and for recreating.\n\nPass the ``--dry-run`` flag to only list the ``max_migration.txt`` files that would be created.\n\nPass the ``--recreate`` flag to re-create files that already exist.\nThis may be useful after altering migrations with merges or manually.\n\n``rebase_migration`` Command\n----------------------------\n\nThis management command can help you fix migration conflicts.\nFollowing a conflicted \u201crebase\u201d operation in Git, run it with the name of the app to auto-fix the migrations for:\n\n.. code-block:: console\n\n    $ python manage.py rebase_migration <app_label>\n\nThe command uses the conflict information in the ``max_migration.txt`` file to determine which migration to rebase.\nIt automatically detects whether a Git merge or rebase operation is in progress, assuming rebase if a Git repository cannot be found.\nThe command then:\n\n1. renames the migration\n2. edits it to depend on the new migration from your main branch\n3. updates ``max_migration.txt``.\n\nIf Black is installed, the command formats the updated migration file with it, like Django\u2019s built-in migration commands do (from version 4.1+).\nSee below for some examples and caveats.\n\nNote rebasing the migration might not always be the *correct* thing to do.\nIf the migrations in your main and feature branches have both affected the same models, rebasing the migration to the end may not make sense.\nHowever, such parallel changes would *normally* cause conflicts in your model files or other parts of the source code as well.\n\nWorked Example\n^^^^^^^^^^^^^^\n\nImagine you were working on your project's ``books`` app in a feature branch called ``titles`` and created a migration called ``0002_longer_titles``.\nMeanwhile a commit has been merged to your ``main`` branch with a *different* 2nd migration for ``books`` called ``0002_author_nicknames``.\nThanks to django-linear-migrations, the ``max_migration.txt`` file will show as conflicted between your feature and main branches.\n\nStart the fix by reversing your new migration from your local database.\nThis is necessary since it will be renamed after rebasing and seen as unapplied.\nDo this by switching to the feature branch ``titles`` migrating back to the last common migration:\n\n.. code-block:: console\n\n    $ git switch titles\n    $ python manage.py migrate books 0001\n\nThen, fetch the latest code:\n\n.. code-block:: console\n\n    $ git switch main\n    $ git pull\n    ...\n\nNext, rebase your ``titles`` branch on top of it.\nDuring this process, Git will detect the conflict on ``max_migration.txt``:\n\n.. code-block:: console\n\n    $ git switch titles\n    $ git rebase main\n    Auto-merging books/models.py\n    CONFLICT (content): Merge conflict in books/migrations/max_migration.txt\n    error: could not apply 123456789... Increase Book title length\n    Resolve all conflicts manually, mark them as resolved with\n    \"git add/rm <conflicted_files>\", then run \"git rebase --continue\".\n    You can instead skip this commit: run \"git rebase --skip\".\n    To abort and get back to the state before \"git rebase\", run \"git rebase --abort\".\n    Could not apply 123456789... Increase Book title length\n\nThe contents of the ``books`` app's ``max_migration.txt`` at this point will look something like this:\n\n.. code-block:: console\n\n    $ cat books/migrations/max_migration.txt\n    <<<<<<< HEAD\n    0002_author_nicknames\n    =======\n    0002_longer_titles\n    >>>>>>> 123456789 (Increase Book title length)\n\nAt this point, use ``rebase_migration`` to automatically fix the ``books`` migration history:\n\n.. code-block:: console\n\n    $ python manage.py rebase_migration books\n    Renamed 0002_longer_titles.py to 0003_longer_titles.py, updated its dependencies, and updated max_migration.txt.\n\nThis places the conflicted migration on the end of the migration history.\nIt renames the file appropriately, modifies its ``dependencies = [...]`` declaration, and updates the migration named in ``max_migration.txt`` appropriately.\n\nAfter this, you should be able to continue the rebase:\n\n.. code-block:: console\n\n    $ git add books/migrations\n    $ git rebase --continue\n\nAnd then migrate your local database to allow you to continue development:\n\n.. code-block:: console\n\n    $ python manage.py migrate books\n    Operations to perform:\n      Target specific migration: 0003_longer_titles, from books\n    Running migrations:\n      Applying books.0002_author_nicknames... OK\n      Applying books.0003_longer_titles... OK\n\nCode Formatting\n^^^^^^^^^^^^^^^\n\n``rebase_migration`` does not guarantee that its edits match your code style.\nIf you use a formatter like Black, you\u2019ll want to run it after applying ``rebase_migration``.\n\nIf you use `pre-commit <https://pre-commit.com/>`__, note that Git does not invoke hooks during rebase commits.\nYou can run it manually on changed files with ``pre-commit run``.\n\nBranches With Multiple Commits\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nImagine the same example as above, but your feature branch has several commits editing the migration.\nThis time, before rebasing onto the latest ``main`` branch, squash the commits in your feature branch together.\nThis way, ``rebase_migration`` can edit the migration file when the conflict occurs.\n\nYou can do this with:\n\n.. code-block:: console\n\n    $ git rebase -i --keep-base main\n\nThis will open Git\u2019s `interactive mode <https://git-scm.com/docs/git-rebase#_interactive_mode>`__ file.\nEdit this so that every comit after the first will be squashed, by starting each line with \u201cs\u201d.\nThen close the file, and the rebase will execute.\n\nAfter this operation, you can rebase onto your latest ``main`` branch as per the previous example.\n\nBranches With Multiple Migrations\n^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\n``rebase_migration`` does not currently support rebasing multiple migrations (in the same app).\nThis is `an open feature request <https://github.com/adamchainz/django-linear-migrations/issues/27>`__, but it is not a priority, since it\u2019s generally a good idea to restrict changes to one migration at a time.\nConsider merging your migrations into one before rebasing.\n\nInspiration\n===========\n\nI\u2019ve seen similar techniques to the one implemented by django-linear-migrations at several places, and they acted as the inspiration for putting this package together.\nMy previous client `Pollen <https://pollen.co/>`__ and current client `ev.energy <https://ev.energy/>`__ both have implementations.\nThis `Doordash blogpost <https://doordash.engineering/2017/05/15/tips-for-building-high-quality-django-apps-at-scale/>`__ covers a similar system that uses a single file for tracking latest migrations.\nAnd there's also a package called `django-migrations-git-conflicts <https://pypi.org/project/django-migrations-git-conflicts/>`__ which works fairly similarly.\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "Ensure your migrations are linear.",
    "version": "2.12.0",
    "project_urls": {
        "Changelog": "https://github.com/adamchainz/django-linear-migrations/blob/main/CHANGELOG.rst",
        "Homepage": "https://github.com/adamchainz/django-linear-migrations",
        "Mastodon": "https://fosstodon.org/@adamchainz",
        "Twitter": "https://twitter.com/adamchainz"
    },
    "split_keywords": [
        "django"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "3702d664ff9d83b9b4ee7211dc66966b934b515cabab8bfe69ec29ba14656d0a",
                "md5": "16e1a16a22b81b11680e82f9db4ed03d",
                "sha256": "816d8c3d407930c94f56c22f89480c4c29186dde13b6b942209e97ccaaf04e13"
            },
            "downloads": -1,
            "filename": "django_linear_migrations-2.12.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "16e1a16a22b81b11680e82f9db4ed03d",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 19284,
            "upload_time": "2023-10-11T09:37:48",
            "upload_time_iso_8601": "2023-10-11T09:37:48.411777Z",
            "url": "https://files.pythonhosted.org/packages/37/02/d664ff9d83b9b4ee7211dc66966b934b515cabab8bfe69ec29ba14656d0a/django_linear_migrations-2.12.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "eac0cf152e4d81ff8814e2738f7d74854412f1d3fcb70b922fc8ca00874686eb",
                "md5": "d62606b4e9043b4360cba8af2ab0dbdd",
                "sha256": "cb6f8dd05de796326af7064eb1822505b16cbbbcb9b2ccef940576e4942d5eda"
            },
            "downloads": -1,
            "filename": "django_linear_migrations-2.12.0.tar.gz",
            "has_sig": false,
            "md5_digest": "d62606b4e9043b4360cba8af2ab0dbdd",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 23478,
            "upload_time": "2023-10-11T09:37:49",
            "upload_time_iso_8601": "2023-10-11T09:37:49.978406Z",
            "url": "https://files.pythonhosted.org/packages/ea/c0/cf152e4d81ff8814e2738f7d74854412f1d3fcb70b922fc8ca00874686eb/django_linear_migrations-2.12.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2023-10-11 09:37:49",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "adamchainz",
    "github_project": "django-linear-migrations",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "django-linear-migrations"
}
        
Elapsed time: 0.14822s