Subject: Re: git switch/restore, still experimental?

From: Ævar Arnfjörð Bjarmason <[email-masked]>
Date: Wed, 05 May 2021 13:09:47 +0200

On Tue, May 04 2021, Elijah Newren wrote:

> On Tue, May 4, 2021 at 3:36 AM Gábor Farkas <[email-masked]> wrote:
>>
>> hi,
>>
>> the "git switch" and "git restore" commands were released two years
>> ago, but the manpage still says "THIS COMMAND IS EXPERIMENTAL. THE
>> BEHAVIOR MAY CHANGE.".
>>
>> i'd love to use them, but this warning gives me pause, perhaps i
>> should wait until it stops being experimental, i worry that it might
>> change in behavior unexpectedly and cause problems for me.
>>
>> considering that they were released two years ago, could the
>> experimental-warning be removed now?
>>
>> thanks,
>> gabor
>
> This probably makes sense.  The author of switch and restore isn't
> involved in the git project anymore.  He decided to work on other
> things, which was and is a big loss for us.  I think others (myself
> included) didn't know all the things that might have been in Duy's
> head that he wanted to verify were working well before marking this as
> good, but these two commands have generally been very well received
> and it has been a few years.  Personally, I'm not aware of anything
> that we'd need or want to change with these commands.

I am.

I think it's quite confusing that "git switch" doesn't switch to a new
"doesnotexist" branch on something like:

    git switch doesnotexist

But requires:

    git switch -c doesnotexist

I mean, I see why. You don't want a typo of "master" as "maaster" to
create a new "maaster" branch, so really that's out. But it really
should be:

    # -n or -N for --new / --new --force (the latter just in case of a
    # race, and just for consistency)
    git switch -n doesnotexist

The design choice of squatting on "-c" for "create" as opposed to "copy"
as implemented in 52d59cc6452 (branch: add a --copy (-c) option to go
with --move (-m), 2017-06-18) has the knock-on effect that we can't
mirror the "git branch" UI. I.e. to make "switch" be "branch with
checkout" for these common cases.

I.e.:

    # copies a branch and config from old->new (or -C for --force)
    git branch -c old new
    # just creates a "new" starting at "old", but no copying!
    git switch -c new old

And:

   # Moves a branch (or -M for --force)
   git branch -m old new

That last one we can't have either because "switch" squats on "-m" for
"--merge", which I daresay is a much more obscure use-case not deserving
of a short option than "rename and switch to".

In summary, I think it should be changed to act like this:
    
    |---------------------------+------------------------+---------------------------|
    | What                      | Now                    | New                       |
    |---------------------------+------------------------+---------------------------|
    | Switch                    | git switch existing    | git switch existing       |
    | Error                     | git switch nonexisting | <no change (errors)>      |
    | Switch with --merge       | git switch -m branch   | git switch --merge branch |
    | Create                    | git switch -c new      | git switch -n new         |
    | Create from existing      | N/A                    | git switch -c new [<old>] |
    | Move & switch to existing | N/A                    | git switch -m new [<old>] |
    |---------------------------+------------------------+---------------------------|

One thing that sucks about my proposal is that it would be squatting on
"-n" for "new" as opposed to "--dry-run".

It would be nice if switch/checkout learned a --dry-run mode, I don't
like e.g. "fetch" having a "-n" that isn't "--dry-run", but can't think
of a better option in the switch case.

In its current state I find "git switch" to be unusable. That sounds
like dramatic hyperbole, but I'm serious.

As much as I applaud the effort to move git's UI forward in this
particular case it's doomed to be only skin-deep because of that
unfortunate initial design choice of sort-of acting like "git branch
with checkout", but squatting on "-c/-C/-m".

I.e. to me the ideal end state would be to deprecate (or at least
warn/discourage) the "git branch -m" case where it does its own checkout
(but for nothing else), and to make "git switch" a "branch with
checkout" with the same -c/-C/-m/-M semantics, just also with a -n/-N
for "create first".

So at the end of the day you still have to use "git branch" for these
common (at least for me) operations of copy/move, *and* maintain a
mental model that "-c" means "xyz" here, but "abc" there.

The "switch" command also solves the very real problem (and I believe
this was the main motivation) of not knowing beforehand if "checkout"
will interpret your "foo" as a file, a branch or whatever. I find it
easier to solve that (I'm aware that it's not a 100% solution) by
consistently using "--" to escape path names, rather than needing to
mentally model the difference in "-c/-C/-m" behavior.