sync
Sync compresses the repetitive development cycle into a single command: pull latest changes, run nbdev’s prepare pipeline (export notebooks to modules, run tests, clean metadata), commit everything, and push. This workflow runs dozens of times during active development—automation prevents skipped steps and inconsistent state.
The sequence is strictly ordered and fails fast. A merge conflict aborts immediately. Test failures block the commit. Each gate ensures the next step operates on valid state.
Future: Extract Git Sync Helper
TODO: Create git_sync() helper for DRY
Both sync() and ship() (in 04_ship.ipynb) perform similar git operations: - Add all changes - Commit with message - Push to remote
Extract this into:
def git_sync(message, verbose=False):
"""Add, commit, and push changes"""
run_cmd(["git", "add", "-A"], verbose=verbose)
run_cmd(["git", "commit", "-m", message], verbose=verbose)
run_cmd(["git", "push"], verbose=verbose)Then both functions can call git_sync(message, args.verbose) instead of repeating these three lines.
Synchronization Workflow
The main sync orchestrator: pull from remote, prepare the project, and push back. Each step validates before proceeding.
sync
sync (args)
Sync project: pull, prepare (export/test/clean), commit, and push
The Sync Gates
Gate 1: Merge conflict detection
When git pull fails, we inspect git status --porcelain for conflict markers. UU indicates both sides modified the same file; AA means both sides added the same file. These require manual resolution—we abort and tell the user to fix conflicts before retrying.
Other pull failures (network issues, auth problems) also abort but with a generic message since we can’t diagnose them automatically.
Gate 2: nbdev_prepare validation
The prepare step runs three operations: nbdev_export (notebooks → Python modules), nbdev_test (run tests), and nbdev_clean (strip notebook metadata). If any fail, we capture and display the full output so the user can see exactly which test failed or which export had issues.
We use capture_output=True here specifically to show output only on failure. Success is silent (unless --verbose); failure is loud.
Gate 3: Nothing to commit
After prepare, git status --porcelain might show no changes—either nothing was modified, or prepare didn’t generate any new files. We detect this and skip the commit step rather than letting git fail with “nothing to commit”.
The commit sequence
We use git add -A to stage all changes: modified files, new files, and deletions. This is essential because nbdev_export might create new module files, and nbdev_clean might modify notebook metadata. The -am shortcut only stages modifications, missing new files entirely.
TODO: When we extract git_sync(), this three-line sequence becomes one function call. Both sync() and ship() need this pattern, so factoring it out eliminates duplication.