You are using the wrong command here.
First, let's note that the word "branch" is ambiguous. It can refer to the data structure in the repository formed by starting from a commit and working backwards in history, or it can refer to a branch name, which is how Git can find the tip-most commit of the branch data structure. That is, the name points to one specific commit, and then from that commit, Git finds its parent commits, and their parents, and so on: the branch data structure. See also What exactly do we mean by "branch"?
git branch
lists branch names
You are correct in thinking that git branch
shows you your current branch:
$ git branch
develop
master
revise
* target
The asterisk *
character shows up in front of your current branch (and, if you have enabled colors, your current branch is shown in green as well, by default, although you can choose other colors).
By default, git branch
shows specifically local (ordinary) branch names. You can ask it for remote-tracking branches with git branch -r
:
$ git branch -r
origin/develop
origin/master
or even "all" branches with git branch -a
.
Branch names are shortened forms; the full names are spelled refs/heads/master
, refs/heads/develop
, and so on. Remote-tracking branch names are spelled with refs/remotes/
, followed by the name of the remote such as origin
, followed by another slash /
, and finally the name of the branch as seen in the other Git repository, on the remote.
Git normally strips off the refs/heads/
part for local branches, and the refs/remotes/
part for remote-tracking branches. This is what we see in git branch
and git branch -r
output. For some mysterious reason, git branch -a
strips only refs/
from remote-tracking branches:
$ git branch -a
develop
master
revise
* target
remotes/origin/develop
remotes/origin/master
git checkout
changes which branch name you are on
Although there are numerous complications, git checkout
is the command to use to switch from one branch to another.
Once you have a local branch, you can use git checkout
to switch to it:
$ git checkout master
Switched to branch 'master'
and so on. Once you're on this other branch, git status
will say On branch master
, and git branch
will put the asterisk next to master
.1
I was working on [a] local branch.
Then I do git fetch origin [new branch that only exists on remote repository]
Don't do that. It's not that you can't do that, it's that writing it out like that is a bad idea. Just run git fetch origin
, which brings over all their branches, and copies them to your remote-tracking branches. Here's what happens when I fetch the latest git
from origin
:
$ git fetch origin
remote: Counting objects: 1892, done.
remote: Compressing objects: 100% (1878/1878), done.
remote: Total 1892 (delta 1199), reused 16 (delta 12)
Receiving objects: 100% (1892/1892), 1.96 MiB | 2.23 MiB/s, done.
Resolving deltas: 100% (1199/1199), done.
From git://git.kernel.org/pub/scm/git/git
05219a1..cf4c2cf master -> origin/master
0b65a8d..2ff7dff maint -> origin/maint
+ 3dc84b0...38a2ea1 next -> origin/next (forced update)
+ b85345d...6eeae5c pu -> origin/pu (forced update)
1ebf750..2b74c92 todo -> origin/todo
This brought over five branches, copying them to refs/remotes/origin/
remote-tracking branch names. It's a bit slower to bring over everything, if all I wanted was master
, but on the other hand, eventually I probably want everything and if I bring it all over now, I won't have to bring it all over later.
It's also a lot easier, and it ensures that I have all the remote-tracking branches, which lets me pick the right remote-tracking branch as my "upstream" for any new local branches I make.
Creating new local branches
If you have a remote-tracking branch, such as origin/newbranch
, and you don't have a local branch that corresponds to it yet, you must create that local branch.
It's a good idea to make sure the local branch:
- has the same name (minus the
origin/
part and in refs/heads/
, of course): in this case we want to create local branch newbranch
;
- starts off in sync with (pointing to the same commit as) the
origin/newbranch
version; and
- has
origin/newbranch
set as its upstream.
We could do this in several steps, by first creating a local branch named newbranch
, then making sure that newbranch
points to the same branch-tip-commit as origin/newbranch
, and finally setting newbranch
's upstream to origin/newbranch
. That's pretty much what you are trying to do now (but you are using the wrong command, and trying to make it all happen as step 2).
Now that we have the remote-tracking branch, however, there is a much easier way:
$ git checkout newbranch
You might, quite reasonably, say: "Hey! That makes no sense at all!" And you are right: there is no local branch named newbranch
, we can't possibly check it out. And yet, with my copy of the repo for Git itself:
$ git checkout maint
Branch maint set up to track remote branch maint from origin.
Switched to a new branch 'maint'
What happens here is that git checkout
starts by trying to check out the local branch maint
, but discovers that it does not exist. So it looks at all our remote-tracking branches and finds that there's exactly one remote-tracking maint
, namely origin/maint
. It then does, all in one command:
- create local branch
maint
- pointing to the same commit as remote-tracking branch
origin/maint
- with
origin/maint
set as its upstream, i.e., tracking maint
on origin
which is what we see in the message.
(And then I don't actually want a maint
, so I did:
$ git checkout master
$ git branch -d maint
to get rid of it.)
git reset
is complicated and not what you want
For completeness, let's look briefly at what git reset
does.
As with git checkout
there are way too many options stuffed into just one command. However, git reset
has, as its primary2 function, the effect of changing the commit to which the current branch points. This is definitely not what you want to do here.
The git reset
command also, optionally:
- with
--mixed
: resets things in Git's "index" (also called the "staging area", which is generally a better name for this)
- with
--hard
: does --mixed
, and removes in-progress work in your work-tree, replacing files with the version re-set into the index.
There are a few more ways git reset
can work, and some of them require that the branch change to its current value (i.e., a no-effect change). These help confuse everyone as to what git reset
does. These probably should have been different Git commands, just as git checkout
's more dangerous modes (that overwrite files) probably should have been separate commands from git checkout
's much safer (never-lose-in-progress-work) forms.
1In Git, you're always "on" some branch. Sometimes, after doing a git checkout
, Git says that you're in "detached HEAD" mode and "not on a branch". When you're in this "detached HEAD" state, you are actually on a branch of sorts, though: you're on the (single, special) anonymous (unnamed) branch. Commits you make while in this state are all temporary, unless you create a name for them "soon enough". (The definition of "soon enough" can be left for a different article / question / blog-post / whatever.) Except when you're in the middle of a git rebase
—and git status
will tell you if you are—if you're in "detached HEAD" mode, you probably want to git checkout
a branch name and get out of this mode.
Both git status
and git branch
will tell you if you're in this detached HEAD state. The git status
command is now very good (in the bad old days of Git version 1.6 or so, it was not so great) and is something you should use frequently.
2I say this is the "primary" effect because git reset
always changes the current branch, though very often, we "change" the commit from where it is now, to where it is now, which is not a very interesting "change". This is because git reset
's other effects, from git reset --mixed
or git reset --hard
, are often what we want. If we let Git move the current branch from where it is, to where it is, and also do a few other things at the same time, the effect is as if it only did the other things.
Since we use git reset
this way a lot, one could call these other effect "primary" as well—but in fact, they're optional, while the branch-moving effect always happens. It's just that if you move the branch from where it is, to where it is, this "move" is very boring and easy to forget.