What is the standard way to create a local branch from a central repo?
Let's say that the central repo has master
, branch_A
and branch_B
. In my local working space, I have only master
and branch_A
.
I want to get a local branch branch_B
in my work-space in a clean way.
ie I want to get the same exact contents from central repo's branch_B
and nothing else.
I thought that git checkout -b my_name/branch_B
would do this. However, when I did this, I got a lot of conflicts, which I do not know why.
I do not want to resolve conflicts manually. All I want is to get the same exact contents from the central repo's branch_B
and nothing else (ie discard any local changes if there is any)
What you want is just git checkout branch_B
even though branch_B
does not exist. In fact, it's critical that it not exist, otherwise git checkout
can't create it.
Before you just use that, though, take a moment to consider this: you're not really "creating a branch from a central repo" at all. You can't: you can only operate on your own repository.
This might seem like a distinction without a difference, but it's critical to making any sense out of numerous bits of Git weirdness. Everything you do, you do to, or in, your own repository.
(The one thing that's sort of an exception—but not really—is git push
. Here, you take things you have already done to/in your own repository, and offer them to another Git, which has its own repository, and then request that it do things to its repository based on what you have offered. It's up to that Git to do those things, to its own repository.)
Centralized repositories
When dealing with a central repository, the way you get stuff from them is to use git fetch
. The fetch
command calls up their Git and requests, from them, a list of their branch-names and other such references, which gives your Git their branch tip commit IDs (SHA-1 hashes). Your Git then requests any commit objects, by hash ID, and any other objects (files, or technically "blobs", tree objects, earlier commits and their trees and blobs, and annotated-tag objects) needed to complete the fetch. Your Git then squirrels all those objects away in your repository, and as a last step, renames all their branch-names to new names that your Git guarantees will not conflict with any of your branch names.
The new names your Git synthesizes from their names are your remote-tracking branches ( origin/master
and so on). These are created and updated by git fetch
. If you use --prune
, or set up pruning as the default, your git fetch
operations will remove from your repository any remote-tracking branch names you have that no longer have a corresponding regular branch in that other Git. (This is sometimes useful, but is not the default, probably for historical reasons plus the "only useful if there's a lot of branch creation and deletion in the other repo" aspect.)
The origin
part of origin/master
is just the name of the remote. A remote is just a name—usually origin
, in fact, since that's the default created by git clone
—under which your Git stores the URL used for git fetch
. The name for these remote-tracking branches is constructed by putting the name of the remote in front of the remote's branch-name: hence master
becomes origin/master
, branch_A
becomes origin/branch_A
, and so on.
Why git checkout
creates branches
The long form of the command you want is:
git checkout -b branch_B --track origin/branch_B
(assuming your other, central, repository is filed away under origin
). The -b
here means "create a branch", and the --track
means: "I'm going to supply another name that I want you, my Git, to look up in my repository to find a commit hash ID. When I do that, I also want you to do the equivalent of git branch --set-upstream-to
once the branch is made." Last, of course, origin/branch_B
is the name of the (already-existing) remote-tracking branch.
This gets used a lot, so the Git folks decided to have git checkout
be extra clever: if you ask Git to check out a branch by name, and it doesn't exist, git checkout
looks to see if there's some remote-tracking branch with the same name, after stripping the remote off the remote-tracking branch name. (Whew!) That is, if you git checkout sneeze
, your Git looks to see if there's an origin/sneeze
or maybe an upstream/sneeze
or whatever.
If there's exactly one such remote-tracking branch, then git checkout
is instructed to create a local branch, using the remote-tracking branch's current hash ID as its starting point.
But the shortcut really has to be spelled exactly that way
Note that git checkout -b branch_B
doesn't make a new branch_B
starting from origin/branch_B
. It just creates a new branch_B
starting from the current commit. The new branch is also not set up to track any remote-tracking branch. This is because -b
tells git checkout
to create the branch immediately, so that it never triggers the "hey, wait, I don't have an existing branch_B
... maybe I should run some automatic special-case code instead?" code.
Of course, for the special case code to work, you must have origin/branch_B
in your repository. It won't reach out and find it on the centralized repository. Only git fetch
will do that—it's just git fetch
and git push
that actually call up another Git, using the URL stashed under the remote's name.
(In fact, git remote
will also sometimes call up another Git, but that's for other SO questions.)
You need to execute:
git checkout -b branch_B origin/branch_B
"origin" is the default name for your remote repository. To know the correct name execute:
git remote -v
First git fetch
After a git fetch
your local repo should have fetched all the branches from the central repo, which usually is called origin
.
This will have created a local remote-tracking branch called origin/branch_B
:
$ git fetch
* [new branch] branch_B -> origin/branch_B
This remote-tracking branch origin/branch_B
tracks in your repo what are the latest changes fetched from central repo.
You're not supposed to commit to it, so you need to create an actual branch to work on.
Then git checkout branch_B
Then simply git checkout branch_B
should create a local branch_B
that automatically tracks origin/branch_B
:
$ git checkout branch_B
Branch branch_B set up to track remote branch branch_B from origin.
Switched to a new branch 'branch_B'
If you want to list both your local and your remote branches, you can use git branch -va
.
下一篇: 从中央仓库创建本地分支的标准方式是什么?