Based on the comments, I believe you had made a single branch clone. Your new clone, where things worked, was an ordinary clone. See How do I "undo" a --single-branch clone? (though these days git remote set-branches origin "*"
is a "better" method for updating things, vs the accepted answer there).
(To find out, run:
git config --get-all remote.origin.fetch
and see what gets printed.)
Meanwhile, let's address these as well:
git fetch --all
This is sometimes useful. It probably wasn't in your case.
git pull --all
This is almost never useful.
git branch --all
This is quite useful and showing the output from this would potentially have helped here. The interesting question is, I think, "why is --all
so inconsistent", or something along these lines. The answer to that is basically that Git is a collection of ad-hoc tools: each one does its own thing, and some of them have useful --all
options and some just don't.
What --all
means to git fetch
is all remotes. In other words, if you have two or three remotes, such as origin
+ upstream
+ fred
, a git fetch --all
means that Git should run:
git fetch origin
git fetch upstream
git fetch fred
(in some order, or perhaps even in parallel).
If you have one remote named origin
, git fetch --all
means git fetch origin
, which is what git fetch origin
and git fetch
also mean. So --all
does absolutely nothing! Most setups have only one remote. (I say this without doing any statistical analysis first, but I'm pretty confident. ) For setups with multiple remotes, git remote update
is probably more appropriate here.
Meanwhile, git pull
means—at one point, it meant this literally as git pull
was just a small shell script—that you'd like Git to run git fetch
for you, and then, if the git fetch
succeeds, run a second Git command for you. The --all
option is passed to git fetch
, where it usually has no effect for the reasons outlined above.
If it does have any effect at all, it has no effect on the second command, and the reason you're using git pull
is to get Git to run the fetch and the second command and you probably care more about the results of the second command. So even if --all
actually did something, you probably don't care. That's why it's almost never useful. (If you see someone advising the use of git pull --all
, you should probably suspect them of cargo cult programming and/or xkcd #1597.)
More about single-branch clones
It's worth exploring what a "single branch clone" really is, why you might sometimes want one, why you probably don't want one, and—perhaps most important—why you might have gotten one without intending to. This gets into the mysteries of Git's refspecs, which are unfortunately somewhat complicated.
When we clone a repository in the ordinary way:
git clone [-b <branch>] <url>
we are telling our Git to:
- make a new empty directory and run the remaining commands in that directory;
- run
git init
to create a new, totally-empty repository;
- run
git remote add origin url
;
- run any additional
git config
or similar operations required;
- run
git fetch origin
; and
- run a final
git switch
or git checkout
to create the branch we gave as our -b
parameter, or the default branch if we didn't give -b
.
Note step 5 in particular. This is what copies all the commits—technically, all reachable commits—from the repository at the given URL. It does not copy any branches though. Instead, it reads out their branch names and changes those names into remote-tracking names in our new clone. If they had branches main
and feature/one
and feature/two
, we end up with remote-tracking names origin/main
, origin/feature/one
, and origin/feature/two
.
A subsequent git fetch origin
reaches back to the same URL and gets, from the Git that responds there, any new commits they have, that we lack, that we need in order to update our remote-tracking names. You can limit this fetch:
git fetch origin feature/two
tells your Git that, despite them maybe having new commits on their main
for instance, you're not interested in those new-to-you commits unless those commits are also on their feature/two
. You'd like to get all the new-to-you feature/two
commits (using their feature/two
, that is), and then when this is all done, update your own origin/feature/two
.
The underlying mechanism that Git uses to do all this is the refspec, and there are some special cases here, because early versions of Git got this wrong. In Git 1.8.2, Git was changed to make this better. It's still downright weird, but it's better.
A refspec is a way of writing two names that will show up in two different Git repositories and relating them, or two patterns for names that will do the same thing. The complete form of a refspec is:
- an optional leading plus sign
+
;
- a left-side name or pattern;
- a separating colon
:
character; and
- a right-side name or pattern.
Both git fetch
and git push
use refspecs, because both git fetch
and git push
connect two repositories. The two repositories each have their own branch names (and other kinds of names) and, for git fetch
in particular, we don't want their branch names to overwrite our branch names. We want their branch names to become our remote-tracking names.
There are a lot of finicky details with refspecs, including the fact that (for historical reasons including that Git 1.8.2 change) fetch and push treat them differently when you leave out the colon. But for the default operation, where you just run:
git fetch origin
for instance, Git has a default fetch refspec. The default value for the origin
refspec is:
+refs/heads/*:refs/remotes/origin/*
and now that we know that the format is +
(optional) <name> :
<name> it's suddenly obvious that the refs/heads/*
on the left means their branches and the refs/remotes/origin/*
on the right means our remote-tracking names.
This is how and why their main
becomes our origin/main
!
So, when you run git clone <url>
, the new repository you get has origin
as its one and only remote—this is why I'm so confident that most setups have one remote, because most people make a clone and then work with it and don't run git remote add
on that to add a second remote—and its remote.origin.fetch
is set to +refs/heads/*:refs/remotes/origin/*
.
But if you use --single-branch
during cloning, step 4 kicks in. Here it is again:
- run any additional
git config
or similar operations required;
What happens here is that we get:
git remote set-branch origin <branch>
or equivalently:
git config remote.origin.fetch +refs/heads/<branch>:refs/remotes/origin/<branch>
This changes the default fetch refspec, so that git fetch origin
or git fetch
only fetches the commits reachable from one branch name in their repository, and only creates or updates the one remote-tracking name.
A two-branch clone
If we make a one-branch clone and then run:
git remote set-branches --add origin branch2
we'll find that git config --get-all remote.origin.fetch
now contains:
+refs/heads/branch:refs/remotes/origin/branch
+refs/heads/branch2:refs/remotes/origin/branch2
This makes use of what Git calls a multi-valued configuration variable. This fetch setting tells git fetch
that by default, fetching from origin
should check both branch
and branch2
for new-to-us commits, bring those over, and update both remote-tracking names.
Using git remote set-branches
without --add
tells git remote
to erase the old settings and install new ones.
A shallow clone is a single-branch clone
Besides single-branch (or two or three-branch) clones, Git offers the --depth
option during cloning, and during git fetch
as well. This makes an extra-limited clone in which Git deliberately doesn't get all the commits initially. Instead, Git fills in the branch graph to the specified depth, from each branch tip commit selected.
In some very large repositories, using --depth 1
speeds up cloning enormously. Most CI systems take advantage of this to make shallow clones for speed reasons, for instance. (Note that, due to a certain degree of silliness in the protocols, --depth 2
is much better if you intend to git push
from this shallow clone. Also, not all implementations of Git protocols offer the ability to push from a shallow clone: e.g., GitHub Desktop apparently never intend to bother implementing it.)
But for no particularly good reason, using --depth
with git clone
turns on --single-branch
automatically. You can use --no-single-branch
to turn it back off. (Why they didn't just make you write --depth 1 --single-branch
in the first place remains a mystery to me.)
The fact that shallow clones are single-branch clones, by default, has bitten many a casual Git user. It's easy to turn a shallow clone into a full clone using git fetch --unshallow
, but if you're not aware that your shallow clone is also a single-branch clone, you don't realize that you must also run git remote set-branches origin "*"
first. (Note the quotes around the asterisk: they are not always required, but are a good idea in general.)