Search code examples
gitgit-branchgit-status

Git status upstream and downstream


I can run these commands and get expected output

$ git branch --track test
$ git checkout test
$ touch hello.txt
$ git add hello.txt
$ git commit -m hello.txt
$ git status
On branch test
Your branch is ahead of 'master' by 1 commit.

However here is where my issue is

$ git checkout master
$ git status
On branch master
Your branch is up-to-date with 'origin/master'.

I want to know if I am ahead of origin/master, but I also want to know if I am behind test. I am looking for a command to give me something like this:

On branch master
Your branch is up-to-date with 'origin/master'.
Your branch is behind 'test' by 1 commit, and can be fast-forwarded.

Solution

  • There is no such thing built in, and tracking is stored only in the other direction: "X tracks Y", not "Y is tracked by ...", which is more complex as the "..." part can expand to more than one item. (Also, I believe it's much more typical for Y to be a remote-tracking branch in the first place, in which case you can't ever be on Y—though a command that tries to find "what tracks Y" could certainly take arguments, so you could say "tell me about any of my branches that track origin/master).

    That said, it's certainly possible to build such a thing. The algorithm looks like this, in Python-esque pseudo-code:

    table = {}
    for branch in local_branches:
        try:
            remote, tracked = get_what_branch_tracks(branch)
        except ValueError:
            continue # local branch "branch" not tracking anything
        try:
            br2 = analyze(branch, remote, tracked)
        except ValueError:
            warn('upstream for %s is gone' % branch)
            continue
        # at this point br2 is, e.g., origin/master or a local branch that "branch" tracks
        table.setdefault(br2, []).append(branch)
    
    # now table[] is a table of branches that are tracked, with
    # each table[key] being the branches that track branch "key"
    

    So now for any interesting branch(es) that are in table, you simply count revisions find-able in the various branch pairs, the same way git status does, which in shell is just:

    # to compute "git status" for branch X that tracks Y:
    nahead=$(git rev-list --count Y..X) # we're ahead $nahead commits
    nbehind=$(git rev-list --count X..Y) # and behind $nbehind
    

    If you're behind but not ahead, you can fast-forward.

    The details for get_what_branch_tracks are simply to do some git config --gets, of branch.branch.remote and of branch.branch.merge, while the details for analyze are more complex: if remote is . then whatever is in tracked is simple, but if it's an actual remote, whatever is in tracked must be passed through the corresponding fetch lines to find the appropriate remote-tracking branch.