Reputation: 21443
I just cloned a repo from bitbucket. For some reason, My local repo does not have all the branches in it. It only has master
. I tried git checkout origin/develop
and ended up with a detached HEAD. git fetch
gives nothing.
I was able to solve it by creating a new branch manually with git checkout -b develop
, then executing git pull origin develop
, but I can't remember ever having to do that before. Usually I can just sync branches from my remotes.
What's going on?
Upvotes: 1
Views: 2211
Reputation: 487983
You did get all the branches, just not in the form you realized.
What you wanted, as Arkadiusz Drabczyk said in a comment, was to run git checkout develop
, which invokes a special shortcut option inside Git—but by running git checkout -b develop
earlier, you broke the shortcut option. :-)
Here's how the whole thing works. It's based on remote-tracking names, which are names like origin/master
and origin/develop
. They have a longer spelling, a sort of full name—instead of just origin/master
, you can type in refs/remotes/origin/master
, for instance—but why type the longer name when the shorter one will do?1
1Maybe the full name is useful for when you're angry or upset, like when the Stella android starts up: HARCOURT FENTON MUDD!
Running:
git clone url
(or the same with a target directory option) is essentially the same as running a bunch of separate commands, most (but not all) being Git commands:
mkdir some-directory && cd some-directory && git init
The directory name here is based on the url
argument: e.g., if you git clone ssh://web.site/path/to/foo.git
you get a directory named foo
. The result at this point is a completely-empty repository, with no branches and no commits; but it does have a configuration.
git remote add origin url
This adds a "remote" to the configuration. The remote's associatd URL is url
. The remote's associated fetch
line—the configured value for remote.origin.fetch
—is set to +refs/heads/*:refs/remotes/origin/*
.
There may be some extra git config
commands here (if you add -c
options to your git clone
, or use some of the other clone options like --bare
). In your case there aren't any here, but I include this on general principles.
git fetch origin
This has your Git call up the Git at the now-configured url
and obtain from that Git, all of its branches and tags. Note that this is the same as any other git fetch origin
command you might run.
git checkout master
It's possible to get Git to use other branch names than master
here, but master
is the most common and most likely. You can control what git checkout
command git clone
runs yourself, but if you don't, you get controlled by the repository you are cloning: they say which branch to check out. If they haven't done anything special, they will say "check out master
", so this last step will check out master
.
fetch
renames their branchesNote that during step 4, git fetch origin
obtains from the other Git a list of all its branch names, and their corresponding commit hash IDs. (To see these directly, without using git fetch
, run git ls-remote origin
.) But git fetch
doesn't save those branch names as your branch names, because your branch names are yours. Saving theirs could mess with your work! So what git fetch
does, by default, is to save their branch names as your remote-tracking names.
If they have a branch named master
, your Git changes this name—the full name is refs/heads/master
; master
is just the short version—to read instead refs/remotes/origin/master
, the full name of the shorter origin/master
. If they have a develop
, you get an origin/develop
, and so on.
In other words, for every branch name branch
that they (origin
) have, you get a remote-tracking name, spelled origin/branch
. The hash IDs for each commit carry across unchanged, but the names change. If you look back at step 2 above, when git clone
added the remote named origin
, you'll see exactly how it is that Git knows to make this particular substitution. The important thing, though, is that it does make the substitution.
Hence, when your clone process finishes, you still have no branches at all. And yet, your git clone
runs git checkout master
, and somehow, that works.
git checkout
command is fancyIf you ask your Git to check out a branch by name, and you don't have a branch by that name, your Git won't just say: Sorry, I don't know that name. Instead, your Git will look at all of your remote-tracking names.
Since you have run git clone
(and maybe some git fetch
-es since then for good measure), you have the remote-tracking name origin/master
, and all the other remote-tracking names that your git fetch
step created or updated. Your Git scans through all of them. If it finds any that look a lot like master
, or whatever branch you asked to check out, your Git will say to itself: Aha, I found exactly one matching name: origin/master
. You must want me to create master
, using the same commit that I see under origin/master
.
This is a "do what I mean" (or DWIM) trick that Git uses when you run git checkout name
but don't have a branch named name. It is shorthand for git checkout -b name origin/name
.
This won't work if you have both an origin/develop
and a remote2/develop
, for instance, because your Git will find two matching names. But to get a remote2/develop
, you'd have to run git remote add remote2 another-url
and then git fetch remote2
. If you haven't added another remote, you can't get two suitable remote-tracking .../develop
s.
But it also won't work for develop
if you've already created a local branch named develop
, because now git checkout develop
will just check out the local develop. You already have it! There's no need—and no ability—to create a new one with the fancy DWIM code.
The git checkout
command will only let you get "on" a local branch. Once you have a local branch named develop
, or create it with git checkout
, the git checkout
command will set things up so that your current branch is the one named develop
. After that succeeds, running git status
will say:
On branch develop
and then print the rest of the status.
A remote-tracking name, such as origin/develop
, won't work for this. Instead, Git will give you that "detached HEAD" mode you saw. Git will check out the commit, but you will be on no branch at all.
To exit this "detached HEAD" mode, you simply have to git checkout
some branch that does exist, or that can and will be created by the DWIM code.
Upvotes: 3