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

Re: master^ is not a local branch -- huh?!?

From
Junio C Hamano <gitster@pobox.com>
Date
Feb 2, 2010, 00:21 UTC
Message-ID
<7v4om0pjwy.fsf@alter.siamese.dyndns.org>
In-Reply-To
<ron1-6F8B85.14520801022010@news.gmane.org>
Ron Garret <ron1@flownet.com> writes:
Show 9 quoted lines
>> For my rather fresh eye it looks more like unnamed (anonymous?) branch
>> than a temporary one. Doesn't detached HEAD behave exactly like a
>> regular HEAD but pointing to the tip of an unnamed branch?
>
> I strongly concur with this.
>
> And as long as I'm weighing in, it would also help to prevent confusion 
> if it were made clear that this unnamed branch doesn't actually come 
> into existence unless and until you do a commit.

After re-reading this three times, I actually cannot tell which one you think is the confused misconception: (1) unnamed branch does not exist until you commit, or (2) unnamed branch does exist immediately you detach.

Let's say you have this history:
    ---A---B HEAD == master

When drawing commit ancestry in ASCII art, uppercase letters in the drawing denote commits. Time flows from left to right and we don't write arrows to show B is a child of A. On the right, above or below commit, we also write refs (i.e. tags, branches and HEAD). When we say "HEAD == master", we mean HEAD is a symref to master ref (if we really want to be anal, 'refs/heads/master' might be more technically correct but most often it is clear from the context).

So by the above picture, we mean "There is a history that ends with B, whose parent is A and it came from somewhere. 'master' branch points at B and HEAD symref points at 'master' so that is the current branch".

Here is what happens when you make changes and "git commit" it.
(0) Normal case.
    ---A---B---C HEAD == master
    The new state is recorded as a tree, a new commit C is created to wrap
    that tree, C is made a child of B (because HEAD pointed at it), and
    finally, master is moved to point at that commit C (because HEAD
    pointed at 'master').

Notice that two "HEAD pointed at" mean slightly different things in the above sentence. In the former context of determining the commit to become the parent of a new commit, we want commit, and "evaluating HEAD by checking at what it points at" wants to return commit, so even though technically HEAD at this point would be:

    $ cat .git/HEAD
    ref: refs/heads/master"

IOW, it points at 'master' branch, we look beyond it and talk about the commit that is pointed at refs/heads/master.

In the latter, we want to determine if there is a branch we would want to update to point at the newly created commit, so we look at HEAD and notice it points at refs/heads/master. We update it, instead of storing the value of C directly in HEAD.

Now, "git checkout master^0" would do this:
    ---A---B---C HEAD (detached)
                 master
There are two pointers.
    $ cat .git/HEAD
    562d53fa69933b3ade2691b99cbe67722313f43c
    $ cat .git/refs/heads/master
    562d53fa69933b3ade2691b99cbe67722313f43c
They point at the same commit C (let's pretend 562d53... is C).
You make changes and create a commit.  What happens?
(1) A new tree is created and wrapped in a new commit D, whose parent is
    C.
                 D    we have not updated
                /     any ref yet
    ---A---B---C
    We used the fact that HEAD points at C (in the first "what commit is
    pointed?" sense) to determine the parent of D.
(2) We decide what pointer to move to point at this commit.  HEAD does not
    point at any branch (it directly pointed at commit C), so we do not
    move any named branch, but move only HEAD.  The end result is:
                 D HEAD (detached)
                /
    ---A---B---C master
    Now you then do "git checkout -b side".  What happens?
(3) We create a new branch "side" at the commit HEAD points at (we could
    have said "git checkout -b side HEAD"), and make HEAD point at that
    branch.
                 D HEAD == side
                /
    ---A---B---C master

Now, when we say "branch", we do not mean the "line" between C and D. "master" branch is not a line before A, between A and B and between B and C concatenated together. "master branch" in git simply points at C in the above graph.

Especailly, there is no special "master"-ness to the line between B and C. B can be reached from both 'master' and 'side' branches. A corollary is that a line between C and D does not have any special 'side'-ness either, as later you can fork other branches from D.

Similarly, in picture (2) where HEAD is still detached, there is no special 'HEAD'-ness to the line between C and D.

Similarly in the picture where you had HEAD that was detached that pointed at C:

    ---A---B---C HEAD (detached)

there is no HEAD-ness in any of the line segment depicted. The same goes for all the lines depicted in picture (2); between these two pictures, the only change made was a commit on the detached HEAD. From the perspective of "branches", there is no change.

Calling detached HEAD as "temporary" or "anonymous" branch is fine, but then we should consider the state immediately after detaching the HEAD equally valid "anonymous" branch as in picture (2).

Putting it in another way, a branch in git is _not_ the name given to line segments that _led_ to the point the branch points at (i.e. past history). Think of a branch as the point where your next commit advances from (i.e. future history).

Previous: Junio C HamanoNext: Nicolas Pitre
Message 21 of 24 in “Re: master^ is not a local branch -- huh?!?”
  1. Steve DiverFeb 1, 2010
  2. Junio C HamanoFeb 1, 2010
  3. Sergei OrganovFeb 1, 2010
  4. Ron GarretFeb 1, 2010
  5. Petr BaudisFeb 1, 2010
  6. Nicolas PitreFeb 1, 2010
  7. Junio C HamanoFeb 1, 2010
  8. Ron GarretFeb 1, 2010
  9. Petr BaudisFeb 2, 2010
  10. Ron GarretFeb 2, 2010
  11. Junio C HamanoFeb 2, 2010
  12. Ron GarretFeb 2, 2010
  13. J. Bruce FieldsFeb 2, 2010
  14. Ron GarretFeb 2, 2010
  15. J. Bruce FieldsFeb 3, 2010
  16. Nicolas PitreFeb 2, 2010
  17. Ron GarretFeb 2, 2010
  18. Nicolas PitreFeb 2, 2010
  19. tytso@mit.eduFeb 2, 2010
  20. Junio C HamanoFeb 2, 2010
  21. Junio C HamanoFeb 2, 2010
  22. Nicolas PitreFeb 1, 2010
  23. Jay SoffianFeb 1, 2010
  24. Steve DiverFeb 1, 2010

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.