You will want to un-single-branch-ize your repository. You have two obvious paths to go by here. One option is to undo the single branch aspect: see How do I "undo" a --single-branch clone? (for which this would be a duplicate of that question). The other requires that you read the git remote
documentation, paying particular attention to the set-branches
sub-command with its --add
sub-option.
Once you've done this, run git fetch
, then git checkout branch-name
or git switch branch-name
. Git will create the new branch. Or, use the remote-tracking name origin/branch-name
. See below for more about remote-tracking names.
Background (optional reading, but a good idea)
It's important to realize that your base assumption here is subtly wrong though:
my remote repository ... was cloned off a feature branch [and] I'm unable to clone [dev
] into my existing local repository
The bad assumption embedded here is that git clone
command doesn't clone a branch. Git's git clone
command is a sort of convenience command, shorthand for running six other commands. Five of those six other commands are Git commands. For illustration, here are the six commands, in the order they are run:
mkdir
(or your OS's equivalent): creates a new, empty directory or folder (whichever term you prefer). The remaining commands are run in this new directory, although once git clone
finishes, you also have to move into this new folder yourself. (There's one mode where this command gets skipped, when you tell git clone
about an existing, empty directory that it should use.)
git init
: this creates a new, empty repository, with no commits. A repository that has no commits cannot have any branches, so it also has no branches.
git remote add origin url
: this adds what Git calls a remote to the empty repository. A remote is simply a short name—here, origin
—that will hold a few things. The main thing it holds is a URL, but it also holds instructions for future git fetch
commands.
The git remote add
sub-command can take a -t
option to specify a particular branch name of interest. The --single-branch
option to git clone
adds this -t
option. (The git remote add
sub-command can also use a name other than origin
. Using -o
in git clone
does this, but we'll assume that you did not use -o
and hence use the name origin
.)
Any other git config
commands implied by -c
options to git clone
. (These -c
options must be at the right place in the command-line arguments: some -c
options are handled by the git
front end rather than the clone
command.) If you did not use such options, git clone
does nothing during this step as there is nothing to do.
git fetch origin
(or other name supplied via -o
): this obtains commits from the other Git, the one that responds to the URL you supplied. This does not create any branch names in your repository. Your new repository still has no branches in it at this point.
git checkout
: this creates one branch in your new repository. The branch it creates is the one you supplied with the -b
option. If you did not supply a -b
option, it creates the branch whose name is recommended by the other Git.
If you use the -n
or --no-checkout
option, git clone
skips this step.
So whether or not you use --single-branch
, step 6 of git clone
creates only one branch. You only have one branch in this new repository! A normal clone copies all commits from the source repository, and none of the branches, and then Git creates one branch as a last step.
What --single-branch
does is just one thing, but has several effects. That's because of the way git fetch
works. At step 5, when git fetch
runs, the fetch command connects to the other Git and its repository. The other Git lists out all of its branch names and other names.1 Your Git can choose to heed all of these names, or just a few of them (e.g., just one branch name). Each of these names also lists a raw commit hash ID, and it's these hash IDs—not the names!—that Git needs. They are how Git finds commits, and other important objects, and they are how your repository clones the other repository.
In any case, having obtained the appropriate commits and other objects, your Git now takes their Git's branch names—the ones they listed to let your Git find commits—and changes them. Your Git stores them—or some selected subset of them, such as exactly one of them—in your own repository as remote-tracking names.
The --single-branch
option tells your Git to leave instructions for fetch
to create or update only the one remote-tracking name corresponding to the one branch name in their Git. The default, of course, is to create or update a remote-tracking name for every branch that the other Git lists out, every time. If you use git remote
to add more of their branch names, each git fetch
will create or update each of those names, provided that the other Git is listing them on each fetch. So by undoing the single-branch-ness, or adding more names, you can get your own Git to create more remote-tracking names.
1The data here can be painfully large—on the order of megabytes—in some repositories with tens of thousands of branch and tag names, and hence there's a way to limit it in the latest Git communication protocols. Older Gits always get everything and the default, for a non-single-branch Git, is to get everything, so that it all still works right, but this does mean that if both your machine and your server have a newer Git version, the single-branch modes can be quite useful in some special cases today.
Branch names and remote-tracking names
A branch name is a name you make up yourself, that should have some meaning to you. This simply stores the hash ID of one (1) commit. By definition, this one commit is the latest commit on the branch. This allows Git to find that commit, and commits in Git allow Git to find more commits in Git. By finding the last one, Git can find all of the commits that go with that branch.
(This means that the word branch is ambiguous: does it refer to the name, such as dev
or main
or whatever? Or does it refer to the collection of commits ended by the latest one? The answer is one, both, or neither, depending on who says it and what they meant when they said it. See also What exactly do we mean by "branch"?)
A remote-tracking name is made up of the name of the remote—origin
—plus a slash, followed by the original branch name.2 These names, like branch names, allow Git to find commits. But they're not actually branch names. You can tell the difference because:
git checkout somebranch
tells you that you are now "on" the branch, and git branch
and git status
tell you that you are now on branch somebranch
(using that exact phrase for git status
). But:
git checkout origin/somebranch
tells you that you are now in what Git calls detached HEAD mode, and git branch
and git status
show that you are no longer on any branch. (This mode is fine for looking at commits, but not a good idea to stay in to do new work. To exit this mode, run git checkout
with the name of one of your branches, as shown by git branch
's output.)
When you ask Git to switch to a branch named B
, Git checks first to see if you have a branch named B
. If you do, Git switches to it.3 But if not, just before saying "I can't switch to that because it doesn't exist", the checkout or switch command (whichever one you used) will see if there's some obvious origin/B
that could be used to create B
. If so, the Git command will create B
using origin/B
,4 then switch to it. This is in fact how step 6 of git clone
works.
2Technically, these remote-tracking names are in a separate namespace. The full spelling of a remote-tracking name begins with refs/remotes/
while the full spelling of a branch name begins with refs/heads/
. Git usually strips off the refs/remotes/
and refs/heads/
parts. Oddly, under git branch -a
, the remote-tracking names are listed with only refs/
stripped off.
3This assumes that said switching is possible. It is possible if you're in a "clean" state, i.e., have not modified anything. It is sometimes possible even if not. See also Checkout another branch when there are uncommitted changes on the current branch
4In fact, any remote-tracking name, even if it starts with something other than origin/
, works here. The "using" part means that your new branch is created using the same commit hash ID stored in your own repository's remote-tracking name.