breaking(but): make name validation in but branch new strict#13929
Merged
Conversation
but branch new strictbut branch new strict
Byron
reviewed
May 22, 2026
Collaborator
Byron
left a comment
There was a problem hiding this comment.
That's a great initiative and I think it will make the CLI much better. I wasn't aware of LBYL, everything seems to have cool acronyms :D.
PS: This message is ignorant of the PR content itself, what's below is just some things that stood out to me.
f5b6b40 to
977cff4
Compare
This solves several problems in `but branch new`: 1. The command would previously normalize branch names automatically, which is unexpected for a CLI. If you type `but branch new 'my branch'` and get a success back, you expect there to be a branch called 'my branch'. But the actual name of the new branch was normalized to 'my-branch'. The new behavior is to reject any non-normalized name and print hint with the normalized name instead. 2. Branch creation was idempotent for branches already applied in the workspace, which was very confusing as the CLI claimed to have successfully created a new branch while in fact it had done nothing. Such branch creation is now clearly rejected, which also falls in line with how Git handles such a situation. 3. Various obtuse error messages have been improved. For example, when creating a new branch with a name that already existed and anchoring it to anywhere, you'd get an error like this:: ``` Error: The reference "refs/heads/<name>" should have content b54c2567115498ffcb87b31a4f1f95f0bead2546, actual content was 6078e6bb61d3b1ac4f35f57f05e2cce3382359ff ``` The new error just says that the branch already exists.
977cff4 to
6a80812
Compare
slarse
commented
May 25, 2026
krlvi
approved these changes
May 25, 2026
6a80812 to
92e211e
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This solves several problems in
but branch new:but branch new 'my branch'and get a success back, you expect there to be a branch called 'my branch'. But the actual name of the new branch was normalized to 'my-branch' (although the output would lie and say it createdmy branch). The new behavior is to reject any non-normalized name and print hint with the normalized name instead.The new error just says that the branch already exists.
Better error messages side quest
Continuing down the path of better error messages to help users out, I've introduced the ability to specify argument name and argument value separately. This allows us to have a standardized way of saying "hey, you done goofed on this argument". I'm not yet convinced exactly when and how to use all this, but it's a start.
For example, when the input is malformed (i.e. the equivalent of a 400 Bad Request HTTP response), I think it's important to clearly connect the dots between the input argument and the badness and try to help the user (or agent) rectify the issue, like this:
However, when the branch name is valid but the state of the repository doesn't allow it to be created (i.e. the equivalent of a 409 Conflict HTTP response), I think we can be much more concise with the amount of information in the output, like so:
Look before you leap
This error handling is LBYL-based, rather than "better to ask for forgiveness than permission" using the new
PreconditionFailedcode. To a large extent I think we'll need to do LBYL-checks for really solid error messages. Here are a few reasons.Not enough information from the underlying implementation
We simply don't have enough error information from the internals to directly correlate the error to the user's input. Even if the response code is
PreconditionFailed, we don't know what precondition. How can we tell if it's the name that's bad, or if it's the anchor?PreconditionFaileddoes not imply user errorIt implies caller error. A trivial example in this piece of code is that we create a canned branch name if the user doesn't pass a branch name. If that fails a precondition, it's still a system error, not a user error. While it's unlikely that we'd create an invalid branch name, it is logically possible and only meant to illustrate the fallacy of assuming that a failing precondition is the user's fault.
What's worse is that a
PreconditionFailedcode only directly implies caller error to the direct caller, as the function that emits the code can only know about its direct inputs. In fact, as long as it's possible that we generate or manipulate input that can cause aPreconditionFailed, the code itself doesn't really add much information to the application as a whole. A caller can never know if the code was emitted from the directly called function, or from a function further down where the inputs aren't necessarily directly controlled by the original caller.To summarize, it is not sound to assume that
PreconditionFailedimplies user error, or even caller error more than one stack frame away.PreconditionFailedis a moving targetRight now,
PreconditionFailedcan only be emitted when the branch name is bad. If you build the code around that assumption, adding anotherPreconditionFailedcause in the internals breaks that assumption without there being any mechanism to find out from the call site.There are downsides to LBYL, too
This is not to say that LBYL error handling is perfect.
But to provide that terrific user feedback, I firmly believe at this point that we need to do input validation immediately, despite these drawbacks. We should also not attempt to do perfect input validation - if we can provide great user feedback for most invalid inputs, and produce obtuse errors for some, that's still a win.
Further notes
but reword, and will thereby replace fix(but): print normalized name after branch rename #13160but branch new refs/heads/my-branchcreates the branchmy-branch. This is inconsistent with Git, which would literally create the branchrefs/heads/my-branch(i.e. with full namerefs/heads/refs/heads/my-branch).