gitgithubversion-control

Show number of unpushed commits on local (non-tracked) branch


I've been working on a local branch for too long without pushing upstream and losing count of how many commits I have locally.

I am now ready to push my commits, but I need the exact number of my commits in order to squash them with git rebase -i HEAD~4 (4 is just an example).

How do I find that number?

I've tried git rev-list --count my-local-branch, but it shows an insanely high number which is not what I want (I have roughly 6 commits).

Also git log --graph --all --decorate --oneline didn't give what I wanted.

I think the problem is that my-local-branch isn't a tracked remote branch.

So I've tried to set that up manually with git branch -u upstream/my-local-branch, but that returns error: the requested upstream branch 'upstream/my-local-branch' does not exist.

To fix that, I could of course just push my branch upstream, but that would defeat my original intent of squashing my commits.

So I feel stuck. Anyone able to advise?


Solution

  • 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.)