Export
central export <repo> -m MSG moves changes made directly in central,
under repo/<repo>/, out into <repo>’s own standalone git repository - as
a single new commit on top of its subrepo branch.
It is the native-OCaml replacement for the one git subrepo command that
sits on central’s critical, day-to-day path: git subrepo push. Unlike
that command, export always squashes whatever changed since the last sync
into exactly one new commit, using the message supplied with -m.
Concretely, it:
- Verifies the subrepo’s
subrepobranch has not moved since the last sync recorded inrepo/<repo>/.gitrepo(if it has, someone likely pushed there directly - bring that in first before exporting). - Computes the diff of everything under
repo/<repo>/since that sync, usinggit diff --relative=, which also strips therepo/<repo>/path prefix from the patch. - Applies that patch as one commit onto the subrepo’s
subrepobranch, viagit apply --3way --index. - Updates
.gitrepoincentralto record the new sync point.
A fake repo to work with
This walkthrough uses Central_test_helpers to build a fake central
repo with a made-up subrepo, widget, and a fake standalone repo standing
in for widget’s own history, wired together with a .gitrepo file
exactly as central subrepo init would leave them.
Right after that, repo/widget/README.md in central and widget’s own
README.md are identical:
# widget
This is a fake [widget] repo, generated by [Central_test_helpers] for tests.
Editing directly in central, then exporting
Suppose someone edits repo/widget/README.md directly from within central
and commits it there - the way most day-to-day changes happen. Running
export then brings that change out, as a single new commit on top of
widget’s subrepo branch:
$ central export widget -m "Document installation"
==================== widget ====================
[ OK ] Applied patch in the subrepo.
[ OK ] Exported to [widget].
It landed in widget’s own history as one new commit on the subrepo
branch, carrying exactly that change:
Document installation
Initial commit
# widget
This is a fake [widget] repo, generated by [Central_test_helpers] for tests.
Added a line about installation.
Guardrails
Running export again right away, with nothing new to export, is a clean
error rather than an empty commit:
$ central export widget -m "Nothing changed"
==================== widget ====================
Error: Nothing to export: no changes under "repo/widget" since the last sync.
[123]
And if the subrepo’s subrepo branch moved since the last sync - for
example because someone pushed to it directly, bypassing central - export
refuses rather than silently basing the new commit on the wrong parent:
$ central export widget -m "Should not apply"
==================== widget ====================
Error: The [subrepo] branch of [widget] has moved since the last sync
recorded in [.gitrepo].
Hint: Bring those changes into central first with [central import] before
exporting, or pass [--force] to export anyway.
[123]
And as a precondition, export first checks that central’s own working
tree is clean - an unstaged edit is rejected outright, before anything else
is even looked at:
$ central export widget -m "Should not apply"
==================== widget ====================
Error: Repo "$CENTRAL_ROOT" has uncommitted changes.
M repo/widget/README.md
Hint: Commit or stash them first.
[123]
Exporting several subrepos at once
Chores that make the same systematic change across many subrepos are common
enough that export accepts more than one REPO at a time (or --all for
every subrepo it knows about) - each is exported in turn, in the order
given, with a separator banner between them, reusing the same -m message
for every commit:
$ central export widget gadget -m "Add license footer"
==================== widget ====================
[ OK ] Applied patch in the subrepo.
[ OK ] Exported to [widget].
==================== gadget ====================
[ OK ] Applied patch in the subrepo.
[ OK ] Exported to [gadget].
Both landed the same commit message on their own subrepo branch:
-- widget --
Add license footer
Initial commit
-- gadget --
Add license footer
Initial commit
central todo picks up both subrepos as done - since export doesn’t
touch central’s own history, both are shown ready for their next
step, advance-main, while central itself needs a push for the
.gitrepo updates:
$ central todo
┌──────────┬──────────────┬──────┐
│ Repo │ Next step │ Diff │
├──────────┼──────────────┼──────┤
│ central │ push │ 12 │
│ gadget │ advance-main │ │
│ widget │ advance-main │ │
└──────────┴──────────────┴──────┘
export stops at the first repo that fails, leaving the ones after it in
the list untouched - it doesn’t try to skip ahead and report a summary at
the end. Say gadget’s subrepo branch moved independently in the
meantime (someone pushed to it directly, bypassing central) while widget
is perfectly exportable:
$ central export widget gadget -m "Add license footer"
==================== widget ====================
[ OK ] Applied patch in the subrepo.
[ OK ] Exported to [widget].
==================== gadget ====================
Error: The [subrepo] branch of [gadget] has moved since the last sync
recorded in [.gitrepo].
Hint: Bring those changes into central first with [central import] before
exporting, or pass [--force] to export anyway.
[123]