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

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Ron Garret <ron1@xxxxxxxxxxx> writes:

>> 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).
--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]