Django Fork Maintenance¶
The mongodb-forks/django repository is a long-lived organisation fork of
django/django. Unlike a personal contributor fork, it carries several
MongoDB-specific branches that each track a different upstream Django release
branch, and it needs to be re-synced periodically as new Django patch releases
land. This page describes how dbx is configured to manage that fork and the
day-to-day maintenance workflow.
How the fork is configured¶
The Django fork lives in the django group and is set up entirely through
config — no --fork-user flag is needed, because the fork is owned by an
organisation rather than a personal account:
[repo.groups.django]
repos = [
"git@github.com:mongodb-forks/django.git",
"git@github.com:mongodb-labs/django-mongodb-backend.git",
"git@github.com:mongodb-labs/django-mongodb-extensions.git",
"git@github.com:mongodb-labs/django-mongodb-project.git",
]
# Clone mongodb-forks/django directly even when --fork is active
no_fork = ["django"]
# Auto-add an `upstream` remote pointing at the canonical Django repo
[repo.groups.django.upstream]
django = "git@github.com:django/django.git"
# Map each local (fork) branch to the upstream branch it tracks.
# Only branches whose upstream stable/<version>.x still exists are listed.
[repo.groups.django.upstream_branch]
django = {"mongodb-6.1.x" = "stable/6.1.x", "mongodb-6.0.x" = "stable/6.0.x", "mongodb-5.2.x" = "stable/5.2.x"}
# Branch checked out automatically right after cloning
[repo.groups.django.preferred_branch]
django = "mongodb-6.0.x"
Each key plays a specific role in fork maintenance:
no_forkLists repos that should be cloned verbatim even when
--forkis active. Becausemongodb-forks/djangois already a fork, it must not have the personalfork_usersubstituted into its URL.upstreamMaps the repo to the URL of the canonical upstream (
django/django). On clone,dbxadds this as anupstreamremote automatically, so no--fork-userflag is required. See Config-Driven Upstream Remotes.upstream_branchMaps each local fork branch to the upstream branch it rebases against. The MongoDB branch names (
mongodb-6.0.x) differ from the upstream Django branch names (stable/6.0.x), so this mapping tellsdbx syncwhich target to rebase onto based on the branch currently checked out. The dict form is what makes multi-branch maintenance work — each branch resolves to its own upstream target.preferred_branchThe branch
dbx cloneswitches to automatically after cloning.
Cloning the fork¶
Clone the whole group (upstream remote and preferred branch are configured automatically):
dbx clone -g django
# Verify the remotes were set up
cd ~/Developer/mongodb/django/django
git remote -v
# origin git@github.com:mongodb-forks/django.git (fetch)
# upstream git@github.com:django/django.git (fetch)
To clone only the Django fork itself:
dbx clone django
Syncing a branch with upstream¶
dbx sync fetches from upstream, rebases the current branch onto its
mapped upstream branch, and pushes the result back to origin. Because the
upstream target is resolved from upstream_branch based on the branch that
is checked out, maintenance is simply “check out a branch, sync it”:
cd ~/Developer/mongodb/django/django
# Bring mongodb-6.0.x up to date with the latest Django stable/6.0.x
git switch mongodb-6.0.x
dbx sync django
# Fetches upstream, rebases mongodb-6.0.x onto upstream/stable/6.0.x, pushes to origin
# Repeat for the next release branch
git switch mongodb-5.2.x
dbx sync django
# Rebases mongodb-5.2.x onto upstream/stable/5.2.x, pushes to origin
Always preview first with --dry-run — it shows the commits that would be
applied from upstream and the commits that would be rebased on top:
dbx sync django --dry-run
If a branch has already been pushed and rebased, the follow-up push may be
rejected; re-run with --force (which uses --force-with-lease for
safety):
dbx sync django --force
Refreshing every release branch¶
When a new round of Django patch releases lands upstream, every MongoDB branch
needs to be rebased. The --all-branches (-b) flag walks the repo’s
upstream_branch mapping for you — it checks out each mapped branch in turn,
rebases it onto its configured upstream target, force-pushes, and restores the
branch you started on. Because rebasing rewrites history, --all-branches
force-pushes by default (using the safe --force-with-lease); you do not need
to pass --force:
# Preview what would change on every mapped branch
dbx sync django --all-branches --dry-run
# Rebase and push every mapped branch
dbx sync django --all-branches
dbx sync django -b # short form
This is equivalent to switching to mongodb-6.1.x, mongodb-6.0.x,
mongodb-5.2.x, … in turn and running dbx sync django on each. The
working tree must be clean for a real sync (each branch is checked out via
git switch), and only branches present in the upstream_branch mapping
are synced.
Combined with --dry-run, --all-branches previews every mapped branch
without checking any of them out: it fetches upstream once and compares each
branch’s origin ref against its configured upstream target directly. Because
nothing is checked out, the preview works even when the working tree is dirty
and never disturbs the branch you have open.
If a branch fails to rebase (for example, conflicts, or an upstream target that no longer exists), that branch’s rebase is aborted so the working tree stays clean and the remaining branches are still processed — one bad branch does not leave a rebase in progress that blocks the rest. At the end, the failed branches are listed so you can rebase them manually:
⚠️ Rebase these branch(es) manually: mongodb-6.0.x
# then, for each listed branch:
cd ~/Developer/mongodb/django/django
git switch mongodb-6.0.x
git rebase upstream/stable/6.0.x # resolve conflicts, then git rebase --continue
Once resolved, re-run dbx sync django --all-branches (already-synced
branches are fast no-ops) to push the remaining branches.
Re-running downstream CI¶
Because the backend’s PR workflows check out the fork branch at a pinned
ref: at CI runtime (see How the fork branches are tested in backend PRs),
force-pushing a rebased fork branch does not re-trigger those workflows — so
a rebase that breaks the adapted tests would go unnoticed until the next push to
the PR. To close that gap, after a successful --all-branches sync dbx sync
re-runs the backend CI for each branch that rebased, via the ci_rerun mapping.
The mapping is keyed per fork branch (only branches that actually rebased are
processed — a branch that failed or was skipped triggers nothing). Each value
maps an owner/name GitHub repo to a target that is either:
an integer PR number — re-runs the workflow runs attached to that PR (needs an open PR with a prior run; updates the PR’s own status checks), or
a string git ref — dispatches the repo’s
test-python*workflows on that backend branch viaworkflow_dispatch(no PR needed). Each backend branch pins the fork branch it tests viaref:, so the backend ref selects which fork branch is exercised — e.g. the backend’smainpinsmongodb-6.0.x. Onlytest-python*workflows that actually declare aworkflow_dispatchtrigger are dispatched; any that don’t (push/schedule/pull_request only) are reported asskipped (no workflow_dispatch trigger)instead of failing.
[repo.groups.django.ci_rerun.django]
"mongodb-6.0.x" = {"mongodb/django-mongodb-backend" = "main"} # dispatch, no PR
"mongodb-6.1.x" = {"mongodb/django-mongodb-backend" = 422} # re-run PR #422
"mongodb-5.2.x" = {"mongodb/django-mongodb-backend" = 562}
"mongodb-6.2.x" = {"mongodb/django-mongodb-backend" = 535}
✨ Done! Synced 3 branch(es)
♻️ mongodb-6.0.x → dispatching CI on mongodb/django-mongodb-backend@main...
test-python.yml ✓ queued
test-python-geo.yml ✓ queued
test-python-replica.yml — skipped (no workflow_dispatch trigger)
♻️ mongodb-6.1.x → re-running CI on mongodb/django-mongodb-backend#422...
#422 ✓ queued (4 workflow run(s))
This uses the gh CLI (GitHub CLI), so gh must be installed and
authenticated. It is best-effort: a missing gh, an unconfigured ci_rerun
mapping, or a GitHub API error is reported as a warning and never fails the
sync. Pass --no-ci to skip the re-run, and note it is skipped automatically
for --dry-run and when no branch actually synced.
Note
The two mechanisms report results in different places. A PR re-run updates the PR’s own status checks, so the result shows up on the PR. A ``workflow_dispatch`` run is standalone — it appears under the repo’s Actions tab, not as a status check on any PR — so after a dispatch you check the run there rather than on a PR.
Choosing between the two: use a PR number for branches that have an open PR
you want to keep green/red (the run attaches to that PR); use a ref to
validate a rebase with no PR involved, or when no suitable PR run exists yet
(for example the backend’s main, which pins mongodb-6.0.x).
Adding a new release branch¶
When Django cuts a new stable branch (for example stable/6.2.x) and you
create a matching mongodb-6.2.x fork branch, add the mapping to
upstream_branch so dbx sync knows where to rebase it:
[repo.groups.django.upstream_branch]
django = {"mongodb-6.2.x" = "stable/6.2.x", "mongodb-6.1.x" = "stable/6.1.x", ...}
Until the mapping exists, dbx sync cannot determine the correct upstream
target for that branch and falls back to upstream’s default-branch detection,
which is almost certainly not what you want for a release branch.
Only map branches whose upstream target actually exists. A branch pointing at a
stable/<version>.x that has not been branched yet (a Django version still in
development on main) or one that has been deleted upstream (an end-of-life
release) will fail to rebase under dbx sync. When a Django release reaches
end of life and its stable branch is removed, drop the corresponding entry
from the mapping.
Running the Django test suite¶
The Django fork uses its own test runner rather than pytest. This is wired up
through test_runner / test_runner_args in the group config:
[repo.groups.django.test_runner]
django = "tests/runtests.py"
[repo.groups.django.test_runner_args]
django = ["-v", "2"]
so dbx test django invokes tests/runtests.py with the configured
arguments. See Testing for details on how custom test runners are
resolved and how to pass additional arguments.
How the fork branches are tested in backend PRs¶
Keeping the fork branches rebased matters because they are the code under test
in every django-mongodb-backend pull request. The backend’s PR CI does not
test against upstream Django — it checks out the corresponding branch of the
mongodb-forks/django fork and runs Django’s own test suite against MongoDB,
using django-mongodb-backend as the database engine. See the open PRs at
https://github.com/mongodb/django-mongodb-backend/pulls.
Each PR triggers several GitHub Actions test workflows
(.github/workflows/test-python*.yml in the backend repo), covering the core
suite plus the geo, Atlas, and encryption feature sets. Those workflows also
declare workflow_dispatch, so they can be run manually (with no PR) on any
backend branch — this is what the ci_rerun “ref” form uses (see Re-running
downstream CI). They all follow the same shape:
Check out
django-mongodb-backendand install it (pip install -e .).Check out
mongodb-forks/djangoat the fork branch for the Django release under test (e.g.ref: mongodb-6.0.x) into adjango_repo/directory, then install it and Django’s test requirements.Copy the backend’s settings files (
mongodb_settings.py, the encryption settings, etc.) and itsruntests.pyintodjango_repo/tests/.Start MongoDB and run Django’s suite via
python3 django_repo/tests/runtests_.py.
In other words, the fork branch supplies the (lightly adapted) Django test
suite, and the backend supplies the database engine and settings. The
ref: in those workflows pins the exact fork branch, so:
A rebased fork branch is what the backend PRs actually exercise — if a rebase breaks the adapted tests, it surfaces as failures on backend PRs, not on the fork itself.
When the backend adds support for a new Django feature release, the fork needs a matching
mongodb-<version>.xbranch (see Adding a new release branch) and the workflows’ref:is bumped to it.Because each backend branch’s workflow pins its own fork
ref:, dispatching a workflow on a given backend ref exercises the fork branch that ref pins — the basis for theci_rerun“ref” form (e.g. the backend’smainpinsmongodb-6.0.x).
To reproduce a backend PR run locally against a fork branch you have rebased,
mirror those steps: check out the fork branch in your django group clone,
install it alongside django-mongodb-backend, copy the backend’s settings /
runtests.py into tests/, and run the suite against a local MongoDB (see
MongoDB Integration).