Here are the last (I hope) pieces of the of puzzle, based on comment below. I requested the output from:
git config --get-all remote.origin.fetch
and that was a single line:
+refs/heads/master:refs/remotes/origin/master
This output is not the normal setting (although it is allowed). The normal setting is:
+refs/heads/*:refs/remotes/origin/*
I think the easiest way to fix it is to run:
git config --edit
which brings up your editor on the Git configuration file for this repository (normally .git/config
). In there you will see these three lines:
[remote "origin"]
url = [whatever the URL is]
fetch = +refs/heads/master:refs/remotes/origin/master
That third line should read:
fetch = +refs/heads/*:refs/remotes/origin/*
i.e., both occurrences of master
should be replaced with *
.
These fetch =
lines control the behavior of git fetch
. Each line provides a refspec, which is little more than a pair of reference names. The existing (dysfunctional) refspec says that your Git should pick up branch master
from origin
(GitHub, in this case) and copy it to your remote-tracking branch origin/master
The corrected refspec says that your Git should pick up every branch (*
) from origin
, and copy each one to a corresponding remote-tracking branch (origin/*
).
(The plus sign at the front tells Git that these references should be copied even if the copy operation is not a fast-forward. For remote-tracking branches, this is what you want.)
Once this is fixed, git fetch origin
will pick up all of the GitHub branches, and all that remains is to make sure that newfeature
, in each of your repositories, has origin/newfeature
set as its upstream. In the repository where newfeature
has master
set as its upstream, run:
git checkout newfeature
git branch --set-upstream-to origin/newfeature
(In any repository that does not yet have a newfeature
branch, see below.)
New answer based mostly on new question in the latest update (which probably really should be a new question). Here's the (new) question again, since otherwise I can't keep straight which part is which:
UPDATE:
I see the newfeature
in the git ls-remote
from my second place:
$ git ls-remote
From git@github.com:me/myproj.git
55e1d6fd9048336c7f0b178fbbf78231ca28ff06 HEAD
55e1d6fd9048336c7f0b178fbbf78231ca28ff06 refs/heads/master
8c4266a6a98f498c129a2a9e806e00e6c6d196b1 refs/heads/newfeature
8c4266a6a98f498c129a2a9e806e00e6c6d196b1 refs/tags/v1
However, I don't know how to use it from my second place:
$ git checkout --track -b newfeature
Branch newfeature set up to track local branch master.
Switched to a new branch 'newfeature'
and git log
is not showing the commits I've published to github.
Maybe I shouldn't have used the -b
?
Indeed, that is the source of the (new) problem.
We can tell because of the second and third line of output:
Branch newfeature set up to track local branch master.
Switched to a new branch 'newfeature'
I have added three different kinds of emphasis here (italic, bold, and bold-italic) so that I can talk about three different points.
First, Git tells us that this is a new branch. That's fine: that's what we wanted! We want to create, in this repository, a new (and local) branch-name.
But we don't want the new local branch to track another local branch. Git us telling us that it's tracking (local) branch master. We want it to track a remote-tracking branch, probably origin/newfeature
.
The command git checkout -b newbranch
tells Git to create a new local branch newbranch
, with no upstream set. Adding --track
modifies the command to tell Git to create newbranch
with some upstream set, but the upstream that Git sets is whatever the current branch is.
Using git checkout --track newbranch
, without -b
, does something quite different; and using git checkout newbranch
, with neither --track
nor -b
, does a third different thing. How we got here is another of those long boring historical-mistake kinds of thing, but in fact, it's usually that third different thing that we want.
Normally we just use git checkout somebranch
to switch to some existing branch somebranch
. This is mostly straightforward: we might be on master
now, when we should be on develop
before we start working, so we git checkout develop
and switch from master
to develop
. (But then Git tries to be helpful, and lets us start changing code first, then switch to develop
. This mostly actually works, and is helpful, but it leads to beginners' confusion: why is it that Git lets me switch now, but if I make another change, it won't let me switch? See Git - checkout another branch when there are uncommitted changes on the current branch for details.)
In yet another attempt to be helpful, Git added a "do what I mean" (DWIM) option to git checkout
so that if you write git checkout newfeature
when there is no newfeature
yet, it will create newfeature
for you, with an upstream set, based on origin/newfeature
. (This is oversimplified; see below.) Hence:
$ git checkout newfeature
invokes the DWIM code ("do what I meant to say, not what I actually said"): Git guesses you meant to use the command:
git checkout -b newfeature --track origin/newfeature
which is the fully spelled out request: "create newfeature
, based on the commit to which origin/newfeature
points right now, and with origin/newfeature
set as the upstream for newfeature
.
That is, it usually does all this for you. The DWIM code is clever, but not perfect. For this to work, the following conditions must all be true:
- Your repository has at least one remote. It can have more than one remote, e.g., you can have
github
and laptop
as your two remotes (in this example neither is named origin
; origin
is merely the standard remote name).
- Your repository has at least one remote-tracking branch that is tracking a branch named
newfeature
on that remote. In this example, if remote-tracking branch github/newfeature
exists, that satisfies this requirement.
- Finally—and this is where things get messed up when you have multiple remotes—your repository must have only one such remote-tracking branch. In this case, if both
github/newfeature
and laptop/newfeature
exist, the DWIM code fails!
Make sure one, and only one, remote-tracking branch exists
It's not clear at this time whether origin/newfeature
actually exists as a remote-tracking branch, in your current repository.
Remember that every Git repository is quite independent of every other Git repository. This means that whether repository G on GitHub has newfeature
does not tell us whether repository L on Laptop has newefature
and/or origin/newfeature
. Moreover, knowing any of these does not tell us whether repository W, on Work-machine, has any of them.
If we're on Work-machine now, in repository W, and we have set up two remotes github
and laptop
, we can run git fetch github
to contact GitHub and pick up newfeature
from repository G (it shows up in the git ls-remote
output) and create or update remote-tracking branch github/newfeature
.
We can also run git fetch laptop
to contact the laptop (assuming the laptop is turned on and connected to the network and reachable). If there is a newfeature
in repository L on the laptop, we acquire laptop/newfeature
.
If we do this, we defeat the DWIM code in git checkout
, because now we have two possible upstreams for git checkout newfeature
.
There's nothing wrong with having multiple remotes and two or more possible upstreams
This setup, with repository W having remotes github
and laptop
, is fine. It just breaks the DWIM code. You can write out what you really meant, and have Git do what you said, rather than relying on Git to guess what you meant to say:
git checkout -b lapfeature --track laptop/newfeature
git checkout -b hubfeature --track github/newfeature
Now, in your work-machine W repository, local branch lapfeature
tracks laptop/newfeature
, which is the code you have committed on your laptop, and local branch hubfeature
tracks github/newfeature
, which is the version you are pushing to github. (These do not have to stay in sync, either ... though if you let them get too far apart, you may make yourself unhappy. :-) )
The DWIM code is convenient though
It's nice to be able to just git checkout newfeature
. To do that, you have to be careful about how many remotes you have. Having just one remote—the usual origin
—makes more of these convenience features work. It's also much clearer when there's just a single Source of Truth, such as "the latest version is always whatever is on GitHub".
The drawback is that you then have to shovel everything back and forth through GitHub. (It's usually up and working, but remember the day GitHub went down?)
Git is confusing, because...
The folks who maintain Git seem to like to shove as much functionality as possible into one command like git checkout
, even when that leads to weird and confusing results like this. Note that git checkout
can:
- switch to an existing branch
- switch to a specific commit ("detached HEAD" mode)
- create a new branch
- create a new "orphan" branch (a branch that is not yet created)
- create a branch's reflog
- extract files from a commit
- extract files from the index/staging-area
- restore a merge conflict (undo the merge resolution from
git add
)
- interactively patch file(s) in the work tree
These are all related—but they are also related to actions like moving a branch (git reset
) and even making diffs (git checkout --patch
has to make a diff). So why are they all in one single git checkout
command? Especially when that leads to complicated situations like "sometimes branch switching is allowed, and sometimes it's not, and git checkout otherbranch
is nondestructive but git checkout otherbranch path/to/file
is highly destructive".
The answer below is for the original question, not the update.
Based on your comment reply, you are looking for methods to work with a remote, with remote-tracking branches, and with branches on a remote. Seems like these should be similar things, if not the same, doesn't it? But they are all rather different, and I think many introductions explain them fairly poorly too. Some of this is no doubt due to history (Git's remotes and remote-tracking branches were new inventions back in 2008-2012; they settled properly in mid to late 2013). If your Git version is at least 1.8.4, and preferably 2.0 or higher, you get to ignore some of the historical problems. (If not, show your Git version—run git --version
—and there will be workarounds, or configuration knobs to set, or something.)
First, some definitions:
- A remote is just a name, like
origin
, under which your Git can store some items. The most important one is a URL, naming where to push to and fetch from.
A branch is ambiguous (see What exactly do we mean by "branch"?).
A branch name is a name like master
or newfeature
; it contains the ID of a (single) Git commit. A regular (local) branch name is a specific form of a more generic Git reference. References have prefixes: local branches are all prefixed with refs/heads/
, so that master
is really refs/heads/master
, and so on.
For the other meaning of "branch", see the linked question. Note that the branch name is updated automatically: making a new commit, while on that branch, causes the branch name to point to the new commit. This is the mechanism by which branches grow.
A remote-tracking branch is a reference that, internally at least, starts with refs/remotes/
. These references are further qualified with the name of the remote, so that all of the remote-tracking branches for origin
start with refs/remotes/origin/
. As with regular branch names, Git normally strips off the prefix, showing you just origin/master
or origin/newfeature
.
Next, with Git—or indeed, any distributed version control system; the same holds for Mercurial, for instance—you must occasionally remember that there are multiple, independent1 repositories, that are not always synchronized. In Git, the synchronization between two repositories happens during git fetch
and git push
. These two commands are the main points2 at which you (and your Git) can actually see the two different repositories at the same time. I think it's educational to run git ls-remote origin
, which reads from the remote but makes no local changes: you should try that now, just to see what it shows you.
A remote-tracking branch has one big purpose: it keeps track, in your own Git repository, of where a branch—a regular, local branch name—pointed, the last time your Git synchronized with theirs. When you run git fetch origin
, your Git contacts their Git, downloads everything necessary, then updates all your remote-tracking branches named origin/*
to match the branches it just saw on origin
.
A git push origin yourcommit:theirname
tells your Git to call up their Git, send to them any commits and other objects required, and then ask them (politely) to please set their branch theirname
to point to the specific commit you identified by yourcommit
. That is, you can git push origin HEAD:name
or—assuming your HEAD
commit is a1234567
—git push origin a1234567:name
and your Git will send commit a1234567
, plus whatever else is necessary, for them to set name
to point to a1234567
. (Your Git and their Git have a conversation about what objects are necessary, right at the start of the push. Note that your Git, their Git, and in fact every Git in the world, always agree on the ID of every commit! This is one of the trickier parts of writing a distributed VCS.)
Usually you will use one of your own branch names, and in this case, you can omit the :theirname
part and your Git will ask their Git to set their branch with the same name. That is, you might git push origin newfeature
, and your Git will send them the tip commit of your branch newfeature
, plus any additional commits and files needed to complete the branch—and then request, politely, that they set their newbranch
.
In general, polite requests are obeyed if this setting is an update that is a fast forward operation, or if it creates the name, or, for that matter, deletes the name. The receiving Git has the chance to say no for any reason, though. (See other SO postings for details.) If the push creates a branch on the remote, this should also create a new remote-tracking branch in your repository, since there is now a new (local, to the remote) branch on the remote, that your Git should track. If the branch already exists on the remote, and the update is accepted, your Git should update your remote-tracking branch.
The push operation can, but does not by default, set your local branch's upstream. See that linked answer for much more about this.
Once the branch exists on the remote, you can use git push
from wherever you have updated it (laptop or work or whatever) to push new commits to the remote, and git fetch
followed by either git merge
or git rebase
to copy commits from the remote. If you have set an upstream, and otherwise configured all the historical baggage configuration knobs correctly, you need not use a lot of arguments to these. (Though you will need to use --force-with-lease
, or --force
/ -f
, or the +
plus-sign prefix syntax in refspecs, to force-push updates that are not fast-forward operations.)
Note too, you can have more than one remote. If you set your laptop as a remote for your work machine, and vice versa, you can transfer commits directly between those machines whenever they are connected to each other on the network (provided you have ssh or similar access—from a Linux box this is generally pretty easy). In other words, you do not have to go through GitHub or some other centralized location (though you can still do that whenever it is convenient).
1In Git's case, they are very independent. In Mercurial, repositories are forced closer together, because branch names are global, in much the same way that Git's tags are global.
2The other ways you can "see both at once", as it were, are git ls-remote
and git remote show
, but both of these are strictly read-only. The fetch
step writes to your repository, and the push
step writes to theirs.