diff --git a/docs/api/commands.rst b/docs/api/commands.rst index 710e192..eb83da4 100644 --- a/docs/api/commands.rst +++ b/docs/api/commands.rst @@ -130,3 +130,11 @@ dbx_python_cli.commands.test module :members: :undoc-members: :show-inheritance: + +dbx_python_cli.commands.worktree module +--------------------------------------- + +.. automodule:: dbx_python_cli.commands.worktree + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/api/utils.rst b/docs/api/utils.rst index a73073e..bce4cf5 100644 --- a/docs/api/utils.rst +++ b/docs/api/utils.rst @@ -34,3 +34,11 @@ dbx_python_cli.utils.venv module :members: :undoc-members: :show-inheritance: + +dbx_python_cli.utils.worktree module +------------------------------------ + +.. automodule:: dbx_python_cli.utils.worktree + :members: + :undoc-members: + :show-inheritance: diff --git a/docs/design/command-structure.rst b/docs/design/command-structure.rst index 953eccf..7ab80e2 100644 --- a/docs/design/command-structure.rst +++ b/docs/design/command-structure.rst @@ -97,6 +97,11 @@ Related subcommands that share a common domain: dbx project list dbx project remove + # Git worktree management + dbx worktree add django --upstream + dbx worktree list django + dbx worktree remove django + Command Groups (Two Levels) ~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/docs/features/django-fork.rst b/docs/features/django-fork.rst index 6d157d0..6414176 100644 --- a/docs/features/django-fork.rst +++ b/docs/features/django-fork.rst @@ -42,6 +42,10 @@ organisation rather than a personal account: [repo.groups.django.preferred_branch] django = "mongodb-6.0.x" + # Add a `django-upstream` worktree on clone so the fork and upstream Django + # can both be worked on from one clone + upstream_worktree = ["django"] + Each key plays a specific role in fork maintenance: ``no_fork`` @@ -65,6 +69,11 @@ Each key plays a specific role in fork maintenance: ``preferred_branch`` The branch ``dbx clone`` switches to automatically after cloning. +``upstream_worktree`` + Lists repos that get a sibling git worktree checked out on the upstream + default branch immediately after cloning. See + :ref:`django-fork-upstream-worktree`. + Cloning the fork ---------------- @@ -87,6 +96,86 @@ To clone only the Django fork itself: dbx clone django +.. _django-fork-upstream-worktree: + +Developing against the fork and upstream from one clone +------------------------------------------------------- + +Fork maintenance regularly needs both sides at once: reading upstream Django to +see what changed, and editing the fork branch to adapt to it. A second clone +would mean a second full fetch of Django's history and two object stores to keep +current. + +A git *worktree* avoids that. One clone can have several branches checked out at +the same time, each in its own directory, all sharing a single ``.git`` object +store. Because ``upstream_worktree = ["django"]`` is set, ``dbx clone`` creates +one automatically: + +.. code-block:: text + + ~/Developer/mongodb/django/ + django/ # primary clone, on mongodb-6.0.x (origin = mongodb-forks) + django-upstream/ # worktree, on upstream-main (tracks upstream/main) + +The worktree's branch is named ``upstream-`` rather than just +``main``, so it cannot collide with a branch of the same name already tracking +``origin`` in the fork. + +Fetching in either directory updates the shared object store, so comparisons +between the two are local and immediate: + +.. code-block:: bash + + cd ~/Developer/mongodb/django/django + git log upstream-main..mongodb-6.0.x # what the fork adds + git diff upstream-main -- django/db/models # what the fork changed + +Managing worktrees manually +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +``dbx worktree`` manages worktrees for any repo, whether or not +``upstream_worktree`` is configured: + +.. code-block:: bash + + # Add the upstream worktree (django-upstream, on the upstream default branch) + dbx worktree add django --upstream + + # Check out a specific upstream branch (django-stable-6.1.x) + dbx worktree add django stable/6.1.x --upstream + + # Check out an existing fork branch alongside the current one + dbx worktree add django mongodb-6.2.x + + # Override the directory suffix + dbx worktree add django stable/6.1.x --upstream --label 61 + + # Show every checkout attached to the clone (* marks the primary one) + dbx worktree list django + + # Remove by directory suffix (defaults to `upstream`) + dbx worktree remove django + dbx worktree remove django 61 --force + +Removal goes through ``git worktree remove`` so the registration inside the +primary clone is cleaned up too; deleting the directory by hand would leave a +stale entry that blocks recreating the worktree later. ``dbx remove`` handles +this as well, removing worktrees before the clone they belong to. + +A branch can only be checked out in one worktree at a time. ``dbx worktree add`` +reports git's refusal rather than working around it, so the fork branch you are +editing is never silently moved out from under you. + +.. note:: + + ``django-upstream`` is skipped by ``dbx sync``, ``dbx switch -g django`` and + ``dbx install -g django`` — see :ref:`upstream-worktrees` for why. It still + appears in ``dbx status``, ``dbx log`` and ``dbx branch``, which only read. + + In particular, the fork clone stays the installed one: a Django worktree has + the same package name, so installing it would replace the fork checkout as + the live ``django`` package in the group venv. + Syncing a branch with upstream ------------------------------ diff --git a/docs/features/repo-management.rst b/docs/features/repo-management.rst index 17e8a41..d24e7e0 100644 --- a/docs/features/repo-management.rst +++ b/docs/features/repo-management.rst @@ -241,6 +241,79 @@ completes — equivalent to running ``dbx sync mongo-python-driver`` right after - Like ``dbx sync``, it's a no-op (with a warning) if no ``upstream`` remote ends up configured for the repo - Pass ``--no-sync`` to ``dbx clone`` to skip this for a single invocation even when configured +.. _upstream-worktrees: + +Upstream Worktrees +~~~~~~~~~~~~~~~~~~ + +A git worktree lets a single clone keep several branches checked out at once, in +separate directories sharing one object store. That makes it possible to develop +against a fork *and* its upstream without cloning the upstream separately. + +List a repo in ``upstream_worktree`` to have ``dbx clone`` create one: + +.. code-block:: toml + + [repo.groups.django] + upstream_worktree = ["django"] + +Cloning then produces a sibling directory checked out on the upstream default +branch, under a branch named ``upstream-`` so it cannot collide +with a same-named branch tracking ``origin``: + +.. code-block:: text + + base_dir/django/django/ # fork clone, on its preferred branch + base_dir/django/django-upstream/ # worktree, on upstream-main + +Manage worktrees for any repo with ``dbx worktree``: + +.. code-block:: bash + + dbx worktree add --upstream # upstream default branch + dbx worktree add # an existing branch, alongside the current one + dbx worktree add -u # an upstream branch + dbx worktree list # every checkout (* marks the primary clone) + dbx worktree remove [