gitq split
Splits one branch into two. Two mutually exclusive modes: --at cuts a branch by commit, --files cuts it by file.
Usage
gitq split <branch> --at <rev> --name <newBranch> [--stack <name>]
gitq split <branch> --files <glob[,glob...]> --name <newBranch> [--stack <name>]
Flags
| flag | meaning |
|---|---|
--at <rev> | Tail-split mode: move every commit after <rev> into the new branch. Any revision git resolves to a commit: short sha, full sha, tag, HEAD~2, branch name. It has to be one of <branch>'s own commits, above where <branch> forks from its stack parent. Mutually exclusive with --files. |
--files <glob[,glob...]> | File-split mode: move files matching the comma separated glob(s) into the new branch. Mutually exclusive with --at. |
--name <newBranch> | Required in both modes. Name of the branch the split mode creates. |
--stack <name> | Which tracked stack <branch> belongs to. Optional when the repo has exactly one; required otherwise. See Global flags. |
Behavior
Refuses with usage help unless <branch> and --name are given and exactly one of --at/--files is (src/cli/commands/surgery.ts:135-137). Both modes are guarded by the stack lease.
--at: pure ref surgery
No working tree is read or written. The new branch is created at the source's current head first (so nothing is ever unreachable, even across a crash), then the source ref is rewound to the split point by compare-and-swap (BranchSplitter.tailSplit, src/core/branch-splitter.ts:64-168). Existing children of the source are reparented onto the new branch, since their base now lives there. Runs directly, without a leased work slot.
- The split point is whatever git resolves.
--atgoes throughgit rev-parse --verify <rev>^{commit}(GitShell.resolveRef,src/core/git-shell.ts:164), so a short sha copied out ofgit log --onelineworks, as do a full sha, an annotated tag,HEAD~2, or a branch name. The^{commit}peel is what lets an abbreviation shared with a blob or tree still land on the commit, exactly as git itself would read it. An abbreviation shared only with blobs or trees resolves to no commit at all, and is reported that way rather than as an ambiguity you could type your way out of. HEAD~ncounts from your checkout, not from<branch>. git resolvesHEADbefore gitq sees it, so standing onmainand running--at HEAD~2names one ofmain's commits, not one of<branch>'s. Spell it<branch>~2to count back from the branch being split.- The split point has to be above the fork. Reachable from
<branch>is not enough: everything at or belowmerge-base(<branch>, <parent>)is the parent's history. Splitting there would rewind<branch>under its own base and hand the parent's commits to<newBranch>, so it refuses instead (src/core/branch-splitter.ts:118-126). - No commit-log window. Whether the resolved commit is on
<branch>is asked of git (GitShell.isAncestor,src/core/git-shell.ts:839), and the moved commits come from the<split>..<branch>range. Age is irrelevant: a commit thousands back is as splittable as the one before HEAD.BranchSplitter.getCommitLogand its 50-commit default are a bounded-log helper with no caller in gitq today; the split does not read it. - Refusals, all exit
1:<branch>not found in the stack;<branch>in the stack but gone from git (Branch "<branch>" is in stack "<id>" but does not exist in this repository);<newBranch>already a node in the stack; the split point is already the branch head (No commits to split — the split point is already at HEAD); the revision is an abbreviation matching more than one commit (Commit "<rev>" is an ambiguous abbreviation (matches <sha>, <sha>); use more characters, always with the candidates, since git only calls an abbreviation ambiguous once it has four hex digits to list them by); the revision resolves to nothing, or to something that is not a commit (Commit "<rev>" does not resolve to a commit in this repository); the revision resolves to a commit that isn't reachable from<branch>(Commit "<rev>" not found in branch "<branch>"), which is now the only case that message covers; the revision resolves to a commit at or below the fork with the parent (Commit "<rev>" (<sha>) is at or below where "<branch>" forks from "<parent>", so splitting there would rewind "<branch>" past its own base and move "<parent>"'s commits onto "<newBranch>". Note that "HEAD~n" counts back from the checked-out branch, not from "<branch>": use "<branch>~n" instead.). - A checked-out source is handled by the slot policy inside the ref finalize (clean: reset along; dirty: refuse), not a separate up-front guard.
--files: builds commits in a work slot
Needs a tree, so it runs detached in a leased work slot; your launch worktree is never touched (BranchSplitter.splitByFile, src/core/branch-splitter.ts:207-330). The new branch is a sibling of the source (a second child of the source's parent), not a child of it, built from the source's merge-base with one commit holding the matched files. The source keeps its own commits, its tip amended to drop the moved files (or rewound to the merge-base if every file moved).
- The patterns match against files the source changed relative to its parent; a pattern matching nothing there is an error, not a no-op.
- Never cascades. Any existing child of the source is left on the old, now-stale head every time; run
gitq syncafterward to bring it forward. - Refusals, all exit
1: no file matches the patterns (No files match the patterns: <patterns>); the branch changed nothing relative to its parent;<branch>or<newBranch>name conflicts, same shape as--at; the source is checked out in a dirty slot elsewhere (branch is checked out in slot "<slot>" (<path>) which is dirty; commit or stash there, or free the slot, then retry). A clean slot holding the source is fine, it gets reset to the rewritten tip.
Exit codes
0: the split completed.1: bad usage, a lease held,<branch>/<newBranch>not resolving, the--atrevision resolving to nothing, ambiguously, to a commit off the branch, or to one at or below the branch's fork with its parent, no files matching the patterns, or the source checked out dirty elsewhere.
Never exits 2. See Exit codes for the general contract.
See also
- Restructure a stack:
--atand--files, both from real runs, with the before/after tree shapes. gitq sync: required after--filesto bring stale children forward.gitq fold,gitq undo:splitis logged but not reversible.