git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: What's cooking in git.git (Jul 2017, #09; Mon, 31)

From
Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Date
Aug 6, 2017, 20:26 UTC
Message-ID
<873794zbls.fsf@gmail.com>
In-Reply-To
<xmqqpoc9sksg.fsf@gitster.mtv.corp.google.com>
On Sat, Aug 05 2017, Junio C. Hamano jotted:
Show 19 quoted lines
> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> We've talked how this UX should look before on-list. Rather than
>> re-hashing the arguments I had before I thought it was useful to present
>> it as a table. Here's how the patch looks now:
>>
>>     |----------------------------+--------------+-------+---------+-----------------+-------------|
>>     | cmd                        | creates new? | moves | copies? | ...with config? | checks out? |
>>     |----------------------------+--------------+-------+---------+-----------------+-------------|
>>     | branch <name>              | Y            | N     | N       | N               | N           |
>>     | checkout <name>            | Y            | N     | N       | N               | Y           |
>>     | checkout -b <name> <start> | Y            | N     | Y       | N               | Y           |
>>     | branch -m <name>           | Y            | Y     | N       | Y               | Y           |
>>     | NEW: branch -c <name>      | Y            | N     | Y       | Y               | Y           |
>>     |----------------------------+--------------+-------+---------+-----------------+-------------|
>
> I actually consider "branch" to *never* invoking a checkout.  Even
> when "git branch -m A B" happens to be done when your checked out
> branch is A and you end up being on B.  That is not a "checkout".

I think we just have a different mental model of what "checkout" means. In my mind any operation that updates the HEAD to point to a new branch is a checkout of that branch.

In the interest of explaining what I mean with the above and not proving you wrong or whatever, consider a basic implementation of git before pack-refs or whatever.

You could then copy a branch name like:
    $ cp .git/refs/heads/{master,master-copy}
Get your currently checked out branch with:
    $ cat .git/HEAD
    ref: refs/heads/master
And you could then implement "git checkout" like:
    $ echo "ref: refs/heads/master-copy" >.git/HEAD

This still works today. Now if I make a commit it goes on the .git/refs/heads/master-copy branch.

Now let's say I wanted to rename the 'master' branch. This would rename it:

    $ mv .git/refs/heads/{master,trunk}
But it wouldn't check it out, my HEAD would still point to master:
    $ git log
    fatal: your current branch 'master' does not have any commits yet
    $ git log --oneline -1 trunk
    8b18718 (trunk) moo
To check it out I need:
    $ echo "ref: refs/heads/trunk" >.git/HEAD
Which yields:
    $ git log --oneline -1 HEAD
    8b18718 (HEAD -> trunk) moo
The mv sans the "echo" is what we'd get if we did:
    diff --git a/builtin/branch.c b/builtin/branch.c
    index 8a0595e115..62ed1a8e20 100644
    --- a/builtin/branch.c
    +++ b/builtin/branch.c
    @@ -493,9 +493,6 @@ static void rename_branch(const char *oldname, const char *newname, int force)
            if (recovery)
                    warning(_("Renamed a misnamed branch '%s' away"), oldref.buf + 11);
    -       if (replace_each_worktree_head_symref(oldref.buf, newref.buf, logmsg.buf))
    -               die(_("Branch renamed to %s, but HEAD is not updated!"), newname);
    -
            strbuf_release(&logmsg);
            strbuf_addf(&oldsection, "branch.%s", oldref.buf + 11);

I.e. the last step of "git branch -m" is checking out the branch by updating the HEAD. Now let's compile git like that and rename "master" to "trunk":

    $ git symbolic-ref HEAD
    refs/heads/master
    $ ./git-branch -m trunk
    $ git symbolic-ref HEAD
    refs/heads/master
    $ git show
    fatal: your current branch 'master' does not have any commits yet

Now let's do the equivalent of that removed replace_each_worktree_head_symref():

    $ git checkout trunk
    Switched to branch 'trunk'
And voila:
    $ git symbolic-ref HEAD
    refs/heads/trunk

This is why I'm saying that "git branch -m" involves a checkout. Because it is literally exactly the case that it could be replaced by a shellscript whose last step is a "git checkout" of the new branch.

Anyway, none of that changes what we /should/ be doing on the fact that "git branch -m" not updating the HEAD would be rather useless. I just wanted to explain what I was talking about and why I was talking about "checkout" in that context.

Show 12 quoted lines
> Really from the end-user's point of view that is not a checkout.
> The user renamed the branch A and the same conceptual entity, which
> is a branch, is now called B.  If that branch was what was checked
> out (IOW, if that branch was what would be grown by one commit if
> the user did "git commit"), then now that branch's name is B.  It is
> natural if you ask "symbolic-ref HEAD" what branch is checked out
> after renaming A to B (and A happened to be what was checked out),
> the answer chould be B.
>
> It's like the city you live in changed the name of the street your
> house is on.  You do not call movers, you do not do anything, but
> your address changes.

Yeah I see what you mean, although this analogy rapidly breaks down when you poke at it as shown above. My house (a sha1) can be on any number of streets and new ones can be added/removed all the time without changing where my house is at.

Show 18 quoted lines
>> I.e. have "branch -c <name>" but just make it not checkout the new
>> thing. What you're describing above sounds to me like:
>>
>>     |-------------------------------------------+--------------+-------+---------+-----------------+-------------|
>>     | cmd                                       | creates new? | moves | copies? | ...with config? | checks out? |
>>     [... stuff above this point is the same ...]
>>     | branch -m <name>                          | Y            | Y     | N       | Y               | Y           |
>>     [... so is branch -m but included for context ...]
>>     | NEW: checkout --super-b -b <name> <start> | Y            | N     | Y       | Y               | Y           |
>>     |-------------------------------------------+--------------+-------+---------+-----------------+-------------|
>
> You are talking backwards.  I do not want "branch -c A B", even when
> A happens to be what is checked out, to check out branch B.  You and
> Sahil were who wanted to check out branch B while doing so, and I
> just tried to guess why you two wanted to have such a behaviour that
> did not make much sense to me.  And my guess was "perhaps they want
> a way to create a new branch starting from another branch, and check
> it out, and do so in a single end-user operation".

The use-case is the following. When I hack on e.g. git.git I'm on "master" with configured upstream info:

    $ git log @{u}..
Because if I don't care about 'master' another shorter way to say:
    $ git checkout -b topic -t origin/master
Is:
    $ git branch -m topic

That's how I start hacking on "topic", now let's say I've submitted that and want to start on topic-2 after getting feedback:

    # While still on topic
    $ git branch -c topic-2

So it's just a way to get something exactly like -m except the "move && checkout" logic is changed to "copy && checkout".

Show 5 quoted lines
> I am not particulary interested in such an operation; in my guess,
> you two are.  And the "super-b" thing was a suggestion to you two:
> If you so desperately want such an operation, then don't make
> "branch --copy A B" that operation.  Such an operation better fits
> in "checkout", not "branch".

That's arguably another sensible way to do a /subset/ of what 'git branch -c' does (or something in addition to this patch). But there's another use-case it won't cover, i.e. the two-arg branch -m/-c manipulating a branch that HEAD doesn't point to:

    $ git checkout master
    $ git branch -m topic avar/topic

You can now do that with -c to s/move/copy/, but couldn't do this with checkout unless you wastefully and needlessly updated the working tree.

> If you are not interested in such an operation, then that is fine.
> Do not add the "super-b" mode to "checkout".

Yeah it's not something I'm interested in or have a use-case for, although I think in the same way we have -t for checkout it might be sensible to have e.g.:

    $ git checkout -b topic-2 -c topic -t origin/master

Where the new -c or --config-from would mean "...and get the config from 'topic'". Such a name would probably be less confusing than --super-b[branch?] which to be implies some ongoing hierarchical relationship.

> But I won't defend a "branch --copy A B" that checks out B when
> users and other people like Ramsay think that such a behaviour is
> illogical, because I do think it is, too.

I just started this from the POV of using branch -m and wanting something like that which didn't remove the old thing when it was done as its last step, which seemed easy to implement & explain.

As noted above I think it probably makes sense to also have something like [--config-from|-c] for checkout, which would have a related but not exactly the same use-case.

Previous: Junio C HamanoNext: Igor Djordjevic
Message 8 of 15 in “What's cooking in git.git (Jul 2017, #09; Mon, 31)”
  1. Junio C HamanoJul 31, 2017
  2. Ramsay JonesJul 31, 2017
  3. Sahil DuaAug 3, 2017
  4. Junio C HamanoAug 3, 2017
  5. Ramsay JonesAug 3, 2017
  6. Ævar Arnfjörð BjarmasonAug 5, 2017
  7. Junio C HamanoAug 5, 2017
  8. Ævar Arnfjörð BjarmasonAug 6, 2017
  9. Igor DjordjevicAug 7, 2017
  10. Igor DjordjevicAug 7, 2017
  11. Ævar Arnfjörð BjarmasonAug 8, 2017
  12. Igor DjordjevicAug 8, 2017
  13. Sahil DuaAug 3, 2017
  14. Stefan BellerAug 3, 2017
  15. Junio C HamanoAug 3, 2017

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.