Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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:

  1. Verifies the subrepo’s subrepo branch has not moved since the last sync recorded in repo/<repo>/.gitrepo (if it has, someone likely pushed there directly - bring that in first before exporting).
  2. Computes the diff of everything under repo/<repo>/ since that sync, using git diff --relative=, which also strips the repo/<repo>/ path prefix from the patch.
  3. Applies that patch as one commit onto the subrepo’s subrepo branch, via git apply --3way --index.
  4. Updates .gitrepo in central to 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]