GitHub
Connect a repo to version your notebooks as readable diffs.
Clusy branches are for live experiments. Git is for versioning the notebook files, a history you can review and roll back. The Git tab in the sidebar does both halves of that: it keeps a local history from the moment you open a project, and it pushes to GitHub once you point it at a repo.
Connecting
Connect GitHub under Settings → Integrations, and choose which repositories Clusy can access rather than handing over your whole account.
Then, from the Git tab, either:
- Connect an existing repo. Pick the account or org, search your repositories, and select one. Clusy tracks that repo's default branch.
- Publish to a new repo. You don't need to have made one first. Pick the account or org, name it (it seeds from the project name), choose private (the default) or public, write the first commit message, and Clusy creates the repo, commits, and pushes in one go. If you want the model weights or datasets in the workspace to go with it, tick include artifacts: the likely model files are pre-selected, and they're tracked with Git LFS.
Publishing artifacts only works on that first publish. An existing repo takes notebook commits only, so weights and large datasets belong in Hugging Face or Kaggle instead.
The Git tab
Before you connect anything, the tab is already keeping a local history: it shows the changed notebooks and lets you commit them with a message. There's just nowhere to push yet.
Once a project is linked to a repo, the tab shows:
- The repo and branch, with how many commits you're ahead and behind the remote, plus Open on GitHub and Disconnect.
- Changes: every notebook file that differs from the last commit, badged added / modified / deleted. Click one and its diff opens as a canvas tab, cell by cell, rather than as a cramped panel in the sidebar.
- A commit box, plus Commit, Push, and Pull.
- History: the commits on the branch.
- Pull requests: the open PRs on the repo, each linking out to GitHub.
A few things it deliberately doesn't do, so you aren't left guessing:
There's no per-file staging. Committing takes every changed notebook. The Changes list is there for reading the diff before you write the message, not for picking a subset.
There's no branch switcher. Clusy commits and pushes on the repo's default branch, the one it was connected to.
Clusy doesn't open pull requests. The tab lists the PRs that already exist and links to them; you open one on GitHub.
When it doesn't go cleanly
Push rejected. If the remote has commits you don't have, the push is refused and Clusy tells you to pull first. (There's an Always force push preference under Settings → Integrations → Git configuration. It does what it says, so leave it off unless you know why you want it.)
Pull conflicts. Pulling is fast-forward only. If your branch and the remote have both moved, Clusy stops and says the branches have diverged rather than attempting a merge. There is no conflict resolution UI. Sort it out on GitHub, or in a local clone, and pull again.
How notebooks are stored
A notebook is committed as a small directory, not as an .ipynb blob:
notebooks/<name>/
manifest.yaml # the branch/fork structure and cell order
main.py # the main branch's cells
branches/<name>.py # each fork's cellsThe .py files are Jupytext percent format: normal Python, one # %% marker per cell. Outputs are not committed. The history is code and structure; the charts, tables, and logs stay in Clusy (and in a share link, which is where they belong when you want someone to see a result).
That's the whole point. Here is a one-line change to a cell, as Git sees it in a raw .ipynb:
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 3,
"metadata": {},
"outputs": [
{
"data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAA4QAAAJYCAYAAADm...",
- "text/plain": ["<Figure size 640x480 with 1 Axes>"]
+ "image/png": "iVBORw0KGgoAAAANSUhEUgAAA4QAAAJYCAYAAEq...",
+ "text/plain": ["<Figure size 640x480 with 1 Axes>"]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
- "model = GradientBoostingClassifier(n_estimators=100)\n",
+ "model = GradientBoostingClassifier(n_estimators=300)\n",
"model.fit(X_train, y_train)"
]
}And the same change, as Clusy commits it:
# %% clusy_id=c8f1a2 clusy_position=4
- model = GradientBoostingClassifier(n_estimators=100)
+ model = GradientBoostingClassifier(n_estimators=300)
model.fit(X_train, y_train)One line changed, one line in the diff. That is what makes reviewing a Clusy notebook in a normal pull request actually work: no re-encoded PNGs, no execution counters churning, nothing to squint through.
What doesn't round-trip
Agent insight and control cells aren't written to the .py files, so they don't
survive a commit-and-clone the way code and markdown do. Neither do outputs.
A workflow that holds up
Explore with Clusy's experiment branches. They're fast, stateful, and throwaway.
Converge on the version you want to keep.
Commit it from the Git tab with a real message, and push.
That keeps the two kinds of branching in their lanes: Clusy branches for the messy exploration, Git for the clean history you keep.