When we do a get pull or git clone, how many local branches are populated with remote-tracking branch? I have seen new branches listed after a pull. Does it mean that all the branches in Git repository are pulled down or just names are listed? The question is more on 'what affects repo and what affects branch?'
-
1Git clone and git fetch, by default, synchronize _all_ the remote data down to your computer. – matt Dec 04 '20 at 07:19
-
if you could add this as answer, it would be great – Blue Clouds Dec 04 '20 at 07:20
-
Do you mean git pull only affects one branch? – Blue Clouds Dec 04 '20 at 07:28
-
@BlueClouds, yes - `git pull` affects just one branch. `git fetch` will get information for all tracking branches but will not do merge; while `git pull` will get information for branch and will try to merge remote into local one. – kosist Dec 04 '20 at 07:40
3 Answers
git clone
and git pull
are completely different commands.
git clone
does the following:
Clones a repository into a newly created directory, creates remote-tracking branches for each branch in the cloned repository (visible using git branch --remotes), and creates and checks out an initial branch that is forked from the cloned repository’s currently active branch.
While git pull
is executed without arguments, it will merge the remote tracking branch into the current local branch.
So yes, after git clone
command all remote-tracking branches are created locally, so they could be checked-out. And, the default branch will be checked out.

- 2,868
- 2
- 17
- 30
You're thinking of branches as if they actually mean something. This is misleading you. Git is not about branches. Git is all about commits. Branch names do matter, but only to the extent that we need them to find commits—and it's possible, in some cases, to find all your commits with no branch names at all.
(There's a secondary issue here, which is that the word branch in Git is ambiguous. See What exactly do we mean by "branch"?)
Now, as kosist answered, git clone
and git pull
are different, but to really see the difference properly, we should break each of those two commands into its constituent parts, because both of them are higher-level commands that work by running lower-level commands.
In fact, git clone
runs six commands—well, up to six—of which five are Git commands. Meanwhile git pull
runs two commands, each of which is a Git command. The second of the two commands that git pull
runs is configurable, but it normally defaults to git merge
; we'll come back to that in a bit. Let's note here that the first of the two commands that git pull
runs is git fetch
. Now let's look at the six commands that git clone
runs:
mkdir
(or some variant for your particular computer/OS): this makes a new, empty directory, after which the remaining five commands run in that new empty directory.git init
: this adds a new empty repository underneath the new directory. The repository itself is the.git
sub-directory in the new directory.git remote add
: this adds a remote, which stores a URL. The stored URL is from yourgit clone
command.git config
(if/as needed): this step is optional and is needed only if you've specified particular configuration values to set in the new clone.git fetch
: aha! this is where we overlap withgit pull
.git checkout
(or in Git 2.23 or later,git switch
): this creates one branch name, and makes it the current commit and current branch. There are some fiddly details here that we'll mostly ignore, but note that until step 6 runs, your new clone had no branches at all.
Again, the overlap is at step 5. The clone
command runs git fetch
and the pull
command runs git fetch
. It is the git fetch
command that obtains new commits from the other Git repository.
A Git repository is a collection of commits
It's time to step back a bit and make a note of what a Git repository really is, at both a high level and at the low level of individual commits.
At a high level, a repository consists mainly of two databases. These two databases are simple key-value stores. The main and usually bigger one has, as its keys, object IDs or hash IDs (Git used to call them SHA-1 IDs, but Git is moving to SHA-256, and eventually, perhaps, pluggable hashes). Commits are one of four kinds of objects, and the one kind that you normally deal with sort-of-directly; all four kinds exist in this database, but two of the other four are there to support commits, and the last one is there to help with annotated tags, which help you (and Git) find commits, so in the end, we're mostly interested in the commits.
The second, usually smaller, database holds names: branch names, tag names, and all of Git's other kinds of names. Each name holds one hash ID. If the name is a branch name, it is required to hold a commit hash ID; other names can use one of those other internal object types, if that's helpful (but we won't look at when it is helpful here as we are concentrating on branch names).
So this is how and why Git is all about commits: everything we do is in the service of finding some commit hash ID, and that hash ID allows Git to look up the commit in the main database. From there, Git can get at what's inside the commit.
What is inside a commit
Once we find a commit by its hash ID, Git gets two things out of it:
Each commit has a full source snapshot. That is, it has, saved for all time (or as long as the commit exists anyway), copies of all the files that Git knew about, at the time you, or whoever, made the commit. That's the data part of a commit: the source snapshot.
Each commit also has some metadata, or information about the commit itself. This includes the name and email address of the person who made the commit, and a date-and-time stamp showing when they made the commit. It includes other things like a log message. Crucially for Git itself, each commit is also allowed to store the hash ID of some number of earlier, already-existing commits. Most commits store exactly one such hash ID.
No part of any commit (or any Git object, really) can ever be changed, so these hash IDs are good forever. The fact that a commit stores the hash ID of some earlier commit, which Git calls the parent of the commit, means that if we have a series of commits in a row, they form a nice simple linear chain, where each commit points backwards, like this:
... <-F <-G <-H
Here H
stands in for the actual hash ID of the last commit in the chain. As long as we somehow know what this commit's ID is, we can have Git find it. Inside H
we'll find the source code snapshot, and also the hash ID of earlier commit G
.
We can thus have Git use H
to find G
. Inside G
, we have a snapshot and an earlier commit ID, so from G
we can find F
.
Inside F
... well, you should have the idea by now. By starting from the end, Git can work backwards, through each commit, all the way to the beginning. The commits in the repository therefore are the history in the repository. All we need to know is the hash ID of the last commit in the chain, and we're good, because Git is all about commits.
Branch names let us find commits
The tricky part is knowing the hash ID. What's the hash ID of the last commit you made? How about some other commit? Do you remember any of them? (I don't remember mine!) We could write them all down or something, but that's silly: we have a computer. Let's have the computer save them.
This is what a branch name does for us: it saves a hash ID. Well, so does any other name in Git: we'll see what's extra-special about a branch name in a moment. For now, let's just say that we have the name main
or master
holding hash ID H
. Then we can draw this as:
...--G--H <-- main
The name lets us have Git find H
, from which we find all earlier commits.
Once we start making new branches, well, let's first make one new name, br1
, that also points to H
:
...--G--H <-- br1, main
We now need to know which name we'd like to use. At the moment it doesn't matter too much: both mean commit H
. But that is about to change. So, let's attach the special name HEAD
, written in all uppercase like this, to one branch name:
...--G--H <-- br1 (HEAD), main
Now, we'll make a new commit, in the way that we do that in Git (which we won't cover at all here). The new commit, being new, gets a new, unique hash ID, different from every other commit hash ID in the universe,1 which we'll just call I
here:
...--G--H
\
I
We need to have the name br1
find commit I
. Git makes this happen by writing the new hash ID into the name to which HEAD
is attached:
...--G--H <-- main
\
I <-- br1 (HEAD)
Nothing happens to HEAD
, but now the name br1
selects commit I
. When we use the name main
, that selects commit H
: H
is the last commit on main
. When we use the name br1
, that selects commit I
: I
is the last commit on br1
.
If we go back to main
and make a new commit (and draw br1
above instead of below for no apparent reason), we get this:
I <-- br1
/
...--G--H
\
J <-- main (HEAD)
and now we can easily see how commits up through H
are on both branches. (If we drew this differently, we might think commits up through H
are only on main
, or only on br1
, but in Git, they really are on both branches. Other non-Git systems are more rigid about branches; there, branches are much real-er than they are in Git. In Git, the names are just there to find "last" commits, and as we saw, there can be commits after the "last" ones: these last ones are just the last ones on the given branch.)
1It only has to be unique within all clones of this repository, but in general, it's universally unique. But SHA-1 is now too small to guarantee this: see also How does the newly found SHA-1 collision affect Git?
Remote-tracking names
When we did that first git clone
, or run the git fetch
step of a git pull
, we have git fetch
run. When git fetch
runs, it:
- calls up some other Git;
- has that Git list out its branch names and other such names; and
- uses the hash IDs from this list to figure out which commits are new, and to get them from the other Git.
Remember, those hash IDs are universal: they're the same in every Git. So our Git can just see whether or not we have the hash IDs that their Git lists out. If we don't have the hash ID, we must need the commit. We ask their Git to send over that commit. Their Git tells us what parent(s) that commit has, and if we don't have them, we get those too—so that in the end, we get all the commits they have that we don't.
Let's say that since an earlier clone, where we got:
...--G--H <-- main (in their Git)
we get, this time:
I--J <-- br1
/
...--G--H <-- main
\
K--L <-- br2
(again these names are in their Git repository, as their branch names). So the new commits are I-J
and K-L
. The parent of J
is I
; the parent of I
is H
; we already have H
in our repository. The parent of L
is K
and the parent of K
is H
. So we ask for them to package up and send over the four new commits.
Once they do, we have, in our repository:
I--J ???
/
...--G--H <-- main (HEAD)
\
K--L ???
We have main
because our Git made it when it ran step 6 of the checkout. We made our main
from their main
. How will we have our Git remember that they had their br1
pointing to J
, and their br2
pointing to L
?
The answer is that our Git takes their branch names and renames them. They become our remote-tracking names. We change their br1
to origin/br1
,2 change their br2
to origin/br2
, and change their main
to origin/main
. In fact, we did that last change earlier—so we already had origin/main
to select commit H
, along with our main
. If we draw all the origin/
names in, we get this:
I--J <-- origin/br1
/
...--G--H <-- main (HEAD), origin/main
\
K--L <-- origin/br2
So now we have an easy way to find all the commits, using these remote-tracking names. We can find any commits we have that they don't—right now, there are none, but if we make some, we'll have some—using our branch names.
2These names actually go into a separate namespace, under refs/remotes/
rather than refs/heads/
.
So git fetch
obtains commits and updates remote-tracking names
What this means is that what git clone
and git pull
have in common is that they run git fetch
, and this gets the commits and updates remote-tracking names. You can run git fetch
in its unlimited mode, as:
git fetch origin
and it will download all new commits and update all remote-tracking names, or you can run it in a more limited way:
git fetch origin br1
and it will only download new commits for their br1
, and update our origin/br1
from their br1
. The git pull
command may use this more limited form of fetch.3
Note: I tend to avoid git pull
entirely myself: I run my own git fetch
, then run a second command of my choice if and when I want to, rather than letting git pull
run git fetch
and then immediately running a second Git command that I have to pick in advance. But this is a matter of taste—you certainly can use git pull
, if you like it.
3Back in the old days, when git pull
was a shell script, it was easier to see what it did: if you ran git pull
with no arguments, it ran git fetch
with no arguments; if you ran git pull origin br1
, it ran git fetch origin br1
; so it was easy to tell how it was going to run git fetch
. It's now a C program and it is harder to see what it does, but the fetch
output tell you what it did.
That second Git command
The second command that git pull
runs is (usually) git merge
or git rebase
. You pick which one you want: git pull --rebase
makes it use the second one (usually).4 Otherwise it uses the one you configured; if you didn't configure one, it uses git merge
.5 Both commands operate on the current branch only, so git pull
only affects the current branch, even if the git fetch
it ran picked up many commits and updated many remote-tracking names.
4This "usually" is because there's a special case: if you run git pull
when on an orphan branch / in a new empty repository, it just runs git checkout
or git switch
as its second command. There are also some cases where it does nothing at all, to avoid wrecking uncommitted work; in the bad old days, git pull
could accidentally wreck such work. That's another reason I still don't use it, having been burned by this rather nasty bug. It's been fixed for more than a decade, I think, but that was a bad burn.
5You can configure it to use git merge --ff-only
, and I for one think this is actually a good idea. A future version of Git may eventually default to this behavior, which is probably the right default. You can also specify more command line options, though many of them are relatively new. Since I don't use git pull
regularly (and have not done so for probably a decade) I'm not sure how new "relatively new" is.
Note that you cannot be "on" a remote-tracking name
The reason to use a branch name is that whole notion of being "on a branch", where when you make a new commit, Git drags the branch name forward to include the new commit.
To get "on a branch", you simply run git checkout
with the name:
git checkout main
or:
git checkout br1
for instance. If the branch name exists now, git checkout
chooses it as the current name, finds the commit that goes with it, and checks out that commit if possible. (If you have uncommitted work, this request might get rejected, if that work would be destroyed in the process.)
If the branch name does not exist, Git has a trick: it will check to see if exactly one remote-tracking name looks a lot like that branch name. If so, Git will create the branch name, using the remote-tracking name as the starting commit. It will then check out that branch name. So git checkout
actually creates new branch names.
This is basically free: branch names in Git have almost no cost. Commits have some cost (often still pretty small), but branch names just point to the commits, so they're super-cheap. Moreover, your branch names are yours. They are not anyone else's. Your own repository holds your branch names. When your Git sees someone else's branch names, your Git turns them into your remote-tracking names instead.
The new (since Git 2.23) git switch
command works the same way here, but has a few new safety features, so if you have this later version of Git, you might want to train yourself to run git switch
instead of git checkout
.
Once you do have a remote-tracking name, though, you might want to try out something like:
git checkout origin/br2
This does work, but it does not put you on a branch. In fact, it takes you off a branch, and puts you into what Git calls detached HEAD mode. You can view a commit this way, and you can even do new work this way, but the doing-new-work part is usually a bad idea. For this reason, git switch
requires that you add --detach
if you are going to use a remote-tracking name:
git switch --detach origin/br2
does the same thing that git checkout
does. The checkout command omits the safety check of requiring --detach
; the switch command keeps it.
We won't go into the details of operating in detached-HEAD mode, but since you're no longer on a branch, the second part of git pull
becomes messy.
Conclusion
git clone
runsgit fetch
, which obtains new commits and updates remote-tracking names. But that's one of six steps.git pull
runsgit fetch
, which obtains new commits and updates remote-tracking names. But that's one of two steps.The big difference between these is in the remaining steps.
Because the second command from
git pull
works on the current branch (only), if you have more than one local branch you want to update, you'll need more than one Git command to update it.For efficiency, you can run one
git fetch
(on your own), followed by multiplegit checkout
and other Git commands:git fetch origin git checkout br1 && git merge git checkout br2 && git merge
for instance. This is roughly equivalent to:
git checkout br1 && git pull git checkout br2 && git pull
except that it's more efficient with CPU and network time. (But which is more expensive: the computer's time, or your time? Maybe the shorter command line is cheaper in the end.)
If all you want to do with a commit is look at it, consider using the "detached HEAD" mode. You do not need your own branch name for this! You can work in Git without branches. Just remember not to make new commits in this mode. (If you do make new commits by mistake, it's easy to recover, though. Nothing committed is lost, at least for 30 days by default, because commits are completely read-only and mostly permanent. The 30 days has to do with commits that you can't find any more: if there is no way to find a commit and it's more than 30 days old, Git will tend to feel free to clean it out.)

- 448,244
- 59
- 642
- 775
- Git clone affects all branches in the repo.
- Git pull affects partially: It will fetch all the branches from the repo but update to latest(merge) if branch exits on the local system for current branch only(default checked-out branch). This is why you saw the name of all the branches listed.

- 7,295
- 4
- 71
- 112