Skip to content

feat(workspace): add clone.branches to clone only the checked-out ref - #1809

Open
knechtionscoding wants to merge 1 commit into
kelos-dev:mainfrom
datagravity-ai:hans/workspace-single-branch
Open

knechtionscoding wants to merge 1 commit into
kelos-dev:mainfrom
datagravity-ai:hans/workspace-single-branch

Conversation

@knechtionscoding

@knechtionscoding knechtionscoding commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What type of PR is this?

/kind api

What this PR does / why we need it:

Workspaces are cloned with git clone --depth 1 --no-single-branch, which downloads the tip of every branch. On repositories with many branches this can be most of a Task's startup time. On a repository with about 6,300 branches, a laptop clone took 122s and 818 MB with today's flags, against 40s and 293 MB with --single-branch.

This PR adds Workspace.spec.clone.branches:

spec:
  repo: https://github.com/example/repo.git
  ref: main
  clone:
    branches: Single   # All (default) | Single
  • All, or omitting the field, keeps today's clone of every branch tip.
  • Single clones only spec.ref, or the remote's default branch when spec.ref is empty. origin is set up to track only that ref, so a plain git fetch origin doesn't pick up other branches. To use another branch, run git remote set-branches --add origin <branch>, then git fetch --depth 1 origin <branch>. The CRD description and docs/reference.md state this.
  • The field has no effect when spec.ref is a full commit SHA, which already uses its own init + fetch path.
  • It applies to Task Jobs and WorkerPools.

The branch-setup init container, which runs when Task.spec.branch or Session.spec.initialBranch is set, now fetches the branch like this:

git fetch --depth 1 origin "+refs/heads/$KELOS_BRANCH:refs/remotes/origin/$KELOS_BRANCH"
  • --depth 1: a plain git fetch origin <branch> into a single-branch shallow clone downloads that branch's entire history. On kelos-dev/kelos that was 1,426 commits against 1. In All mode the tip is already present, so the change is unconditional.
  • Explicit destination: keeps origin/<branch> present, as the all-branches clone does today, so agents that read origin/<branch> keep working.
  • Tracking (Single only): branch-setup first runs git remote set-branches --add origin "$KELOS_BRANCH". Without it, origin's fetch refspec covers only the cloned ref. After an agent pushes its branch, origin/<branch> stays at the old commit and git push -u sets no upstream. With it, later fetches and pushes keep origin/<branch> current. All mode doesn't need it, so the line is only added for Single.

Which issue(s) this PR is related to:

N/A

Special notes for your reviewer:

  • Struct, not a bool. I chose clone: { branches: All | Single } instead, so the default can change later without reinterpreting what users wrote. It also gives future clone settings a place to go (depth, filter, sparse paths; see New use case: Monorepo support with workspace scoping, sparse checkout, and per-service agent routing #518) without new top-level fields. Only branches is added here. Single matches git's --single-branch, which is the term users will search for.
  • merge --ff-only became reset --hard FETCH_HEAD for the case where the Task branch already exists locally, which only happens when it is the cloned branch. After a --depth 1 fetch, the old local tip is not an ancestor of the new shallow tip. So if the remote moves between clone and fetch, --ff-only fails and the init container fails, in both modes; I confirmed this with git locally. Resetting is safe because branch-setup only runs on a freshly cloned repo: Task workspaces are emptyDir, and Session init re-clones until the workspace is marked initialized, then skips branch-setup.
  • Conversion. The field exists only in v1alpha2. Workspace conversion is plain JSON, so a v1alpha1 round-trip drops it, as it already drops ghproxy. A dropped value falls back to All, which is slower but correct.
  • e2e. kelos-dev/kelos has no non-main branch that is guaranteed to last, so the e2e test clones tag v0.59.0 with branches: Single and sets spec.branch: main. That makes branch-setup fetch a branch that isn't in the clone. A setupCommand prints the git state. The test checks that the repo is shallow, main is checked out at origin/main, only one remote branch exists, and origin's fetch refspecs include main. I ran the same steps locally against GitHub and they passed.
  • Tests.
    • Unit: exact clone arguments for Single and All, a commit-SHA ref ignoring the field, the full fetch and reset lines in the branch-setup script, the set-branches line present for Single and absent for All and commit refs, and WorkerPool clone arguments in both modes.
    • Integration: a Task with branches: Single, plus a check that an unknown value is rejected.
    • The existing exact-argument assertions for the default are unchanged.

Does this PR introduce a user-facing change?

Workspaces accept `spec.clone.branches`. Set it to `Single` to clone only the checked-out ref instead of the tip of every branch, which speeds up Task startup on repositories with many branches. The default, `All`, keeps the current behavior.

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 11 files

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

Comment thread internal/controller/job_builder_test.go
Comment thread docs/reference.md Outdated
Comment thread internal/manifests/charts/kelos/charts/kelos-crds/templates/workspace-crd.yaml Outdated
Workspaces are cloned with `--depth 1 --no-single-branch`, which downloads
the tip of every branch. On repositories with thousands of branches this
dominates task startup.

Add `Workspace.spec.clone.branches` (`All` | `Single`). `Single` clones
with `--single-branch`, so only `spec.ref` (or the default branch) is
fetched and origin is configured to track only that ref. Omitting the
field keeps today's all-branches clone.

The branch-setup init container now fetches the Task branch with
`--depth 1` and an explicit `refs/remotes/origin/<branch>` destination.
Without `--depth 1`, fetching a branch into a single-branch shallow clone
downloads its full history; the destination keeps `origin/<branch>`
available as the all-branches clone did. In `Single` mode it also adds
the Task branch to origin's fetch refspecs, so later fetches and pushes
keep `origin/<branch>` current and `git push -u` sets an upstream.

Because a shallow fetch cannot fast-forward an existing local branch when
the remote moves between clone and fetch, the existing-branch case resets
to FETCH_HEAD instead of `merge --ff-only`. branch-setup only runs on a
freshly cloned repository, so there are no local commits to preserve.
@knechtionscoding

Copy link
Copy Markdown
Contributor Author

bump @gjkim

1 similar comment
@knechtionscoding

Copy link
Copy Markdown
Contributor Author

bump @gjkim

@knechtionscoding

Copy link
Copy Markdown
Contributor Author

@gjkim42 bump here

This branch is waiting to be deployed

1 waiting deployment
ok-to-test — 0132f532 Waiting Oct 8, 2026 by knechtionscoding via fork-e2e / e2e-with-environment #1866
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

kind/api Categorizes issue or PR as related to API changes needs-actor needs-priority needs-triage release-note

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant