Choosing a task's base branch
Set which branch a task's sandbox is cut from and where its merge request lands — at creation, afterwards, for schedules, and when you don't set one.
Every task builds its change on top of some starting point in your repository (or repositories — a task can work in more than one, see Tasks that span several repositories). By default that’s each repo’s own default branch — usually the same branch a fresh git clone checks out, unless your workspace has set a different one. A task’s base branch lets you override that with a single name, applied to every repo the task works in, so its sandbox branches from — and its merge requests target — wherever you name instead.
Reach for this when a task belongs on a release branch, a long-lived feature branch, or anything other than the branch most work builds on.
Setting a base branch at creation
Pass --base-branch to ch task create:
ch task create --title "Backport the auth fix" --type story --base-branch release/2.1
From the web app, the New task form has a Base branch field. Leave it blank — its placeholder text reads (repo default) — and the task uses your repo’s default branch, same as if you’d omitted the flag on the CLI.
On creation, --base-branch "" isn’t the same as leaving the flag out. CodeHerder refuses it and asks you to either pass a real branch name or omit the flag entirely. Empty-string-to-clear only makes sense once a task exists to clear it on — see the next section.
Changing it afterwards
Set or clear a base branch on an existing task with ch task edit:
ch task edit <taskId> --base-branch release/2.1
Pass an empty string to clear it and fall back to the repo default. This is the one place --base-branch "" works — it’s an edit-only form:
ch task edit <taskId> --base-branch ""
From the web app, the task page has its own editable Base branch row, next to the task’s other details — click it to set or clear the value the same way.
ch task show displays the current base branch when one is set (and the task page in the web app always shows it, including the repo-default case). ch task edit also echoes back what it just set, or cleared, when you change it.
What counts as a valid branch name
CodeHerder checks a base branch name the moment you set it, whether at creation or with ch task edit — not only once a sandbox tries to use it. A name is refused if it:
- contains a space or tab inside the name,
- contains any of
: * ? [ \ ^ ~, - contains two dots in a row (
..) or the sequence@{, or - starts with a
-.
A rejected name comes back as an error right away, so you can fix it before it ever reaches a sandbox.
It’s fixed once work starts
You can only change a task’s base branch before it has a sandbox. Once one exists, the branch is already cut from wherever it started, and CodeHerder refuses the change:
edit task: HTTP 409: baseBranch cannot change while the task has a live sandbox; the worktree is already checked out from the current base
Set the base branch before the task is staffed if you know it needs one. If a task has already started on the wrong branch, file a fresh one with the right --base-branch instead of trying to redirect the one in progress.
Where CodeHerder starts from
When a task’s sandbox is provisioned, CodeHerder picks the starting ref for each repo it works in, independently, in this order:
- A base ref named directly on that one sandbox, if you provisioned it yourself with
ch sandbox create --base-ref <ref>— this always wins, overriding the task’s own base branch. - The task’s own base branch, if one is set — the same name applies to every repo in the task’s set.
- That repo’s own default branch, if neither of the above is set but the repo has one.
- Whichever branch that repo’s own remote HEAD points at, if none of the above is set.
So on a task spanning several repos, a base branch named on the task is tried against every one of them; a repo without its own default branch falls back independently to its own remote HEAD, regardless of what the others do. CodeHerder resolves this fresh every time it provisions a sandbox, against a live read of each repo — so changing a repo’s default branch later, or clearing a task’s own base branch, takes effect the next time that task needs a new sandbox.
A base branch that doesn’t exist yet
Name a base branch that isn’t on a repo’s remote yet, and CodeHerder creates it for you on that repo — cut from that repo’s own default branch and pushed — before the task’s sandbox is provisioned. This applies independently to every repo the task works in: a repo whose remote already has the branch is left alone, and only a repo that’s missing it gets the new branch created for it. It only ever does this for a branch you named on the task itself; it never invents one on your behalf.
It won’t create one in a few cases:
- the name is already taken by a tag on that repo’s remote,
- the name looks like a raw commit ID rather than a branch name,
- the repo’s own default branch can’t be determined, or
- the name you gave is that repo’s own default branch, so there’s nothing to create.
Any of these fails the task’s sandbox provisioning with a clear error, so you can rename the task’s base branch and try again.
If another device or person creates the same branch while CodeHerder is doing this, that’s not treated as a failure. CodeHerder picks up whatever the branch points to on the remote and carries on provisioning.
Schedules and copies
A task schedule can carry its own base branch, set the same way as on ch task create. Every task the schedule spawns is born with it, so recurring work keeps landing on the branch you chose without you having to set it each time. See Scheduled tasks for setting one up and everything else a schedule carries.
Copying a task (ch task copy) carries the source task’s base branch over, along with its title, type, and repo set. Turning a task into a schedule (ch task schedule) carries the base branch and repo set over too.
What else follows the base
The merge request a task’s build stage opens for each repo targets that task’s base branch, not necessarily the repo’s own default branch — so review, merge, and verify all happen against it too. See How work flows for the full pipeline these stages run.
Related guides
- Writing tasks an agent can build — set a base branch and everything else at creation
- Editing and cancelling tasks — every other native field you can change after creation
- Connecting repositories — set and change a repo’s own default branch
- Tasks that span several repositories — how a task’s repo set works, and how the base branch resolves for each one
- Scheduled tasks — give a schedule its own base branch so every task it spawns inherits it
- Start a session on a device — a
--base-refyou set directly on a session, outside any task
Last updated