I think your fundamental problem here may lie in the way you are viewing the "branchness of commits" (to make up a phrase). Or, it may just be that you're limiting your concept of "upstream".
Let me illustrate with this:
...--o--o--o--o <-- master, origin/master
This is a drawing of at least four commits on branch master
, with master
in sync with origin/master
.
...--o--o--o--o <-- origin/master
\
o <-- master
This is the same repository after adding one commit on master
that is not on origin/master
, so that branch master
is "ahead 1", as git status
would put it.
Now let's put master
back where it was, but keep that new commit, having added it on newbr
which we made with git checkout -b newbr
instead of just adding it on master
:
...--o--o--o--o <-- master, origin/master
\
o <-- newbr
You want Git to be able to tell you that newbr
is "1 ahead" of something. The problem lies in defining the "something".
Here's the fundamental trick: all those commits that are on master
and also on origin/master
, are also on newbr
. Commits can be on many branches at once, including multiple local and remote-tracking branches.
The way git status
calculates the "ahead" and "behind" numbers is to use git rev-list --count
, and use the "upstream" setting to exclude commits that are on both the current branch and the upstream. So in our second illustration, when master
was "ahead 1", both master
and origin/master
had at least the original four commits, but master
had one more than origin/master
.
(What git status
is doing is git rev-list --count origin/master..master
, i.e., count everything that remains after selecting all commits on master
minus all commits on origin/master
. Of course instead of a literal master
, it uses your current branch, and instead of a literal origin/master
, it uses your current branch's upstream.)
What this means is that, to get git status
to report things like this, we must choose—at least temporarily—some branch, perhaps even a local branch rather than a remote-tracking branch, to set as the "upstream" for newbr
.
In this particular drawing, both master
and origin/master
are suitable values for the upstream in git branch --set-upstream-to
. Your repository will differ somewhat, but there will be some name for the commit you would like your Git to start excluding. (Even if there isn't, you can simply make a new branch for the exclusion point. But there's almost always one out there already.)
Use git log --graph --oneline --decorate --all
to get Git to draw a text version of these graphs (vertically, instead of horizontally) with labels showing which branch and tag names point to which commits. This usually makes it visually obvious which branch name(s) are suitable for the upstream name.
Remember, local branches work fine as an upstream, as long as you remember to re-set the upstream once you have the appropriate remote-tracking branch.
(Incidentally, if you don't like using a local branch as your upstream for your new branch, you can create the branch on your remote, pointing to the commit your branch "starts from". For instance, in the example illustration above, you might want origin/newbr
to point to the same commit as master
and origin/master
. So you can use:
git push origin master:newbr
to create newbr
on remote origin
, pointing to the same commit your local master
points to. Once you've done that, origin/newbr
now exists, so now you can set it as the upstream for newbr
.
If there's a more specific commit, identifiable only by its hash ID, you can even use that:
git push origin a123456:refs/heads/newbr
When identifying a commit by its raw SHA-1 hash, you should spell out full reference names like this, since Git can no longer use your local name to figure out whether you intended a branch or a tag.)