45

I would like to extract the information that is printed after a git status, which looks like:

# On branch master
# Your branch is ahead of 'origin/master' by 2 commits.

Of course I can parse the output of git status but this is not recommended since this human readable output is liable to change.

There are two problems:

  1. How to know the remote tracked branch? It is often origin/branch but need not be.
  2. How to get the numbers? How to know whether it is ahead/behind? By how many commits? And what about the diverged branch case?
jamessan
  • 37,987
  • 8
  • 79
  • 85
Olivier Verdier
  • 41,410
  • 26
  • 94
  • 89
  • 1
    How about this answer.. http://stackoverflow.com/questions/7773939/show-git-ahead-and-behind-info-for-all-branches-including-remotes – rh0dium Sep 14 '12 at 15:29
  • For Git 1.9/2.0, see also http://stackoverflow.com/a/20499690/6309 – VonC Dec 10 '13 at 16:23
  • With Git 2.5+, the actual command would be: `git for-each-ref --format="%(push:track)" refs/heads`. See [my answer below](http://stackoverflow.com/a/30720387/6309). – VonC Jun 08 '15 at 22:53
  • If you don't necessarily want to compare against the tracking branch, but any two branches, I believe this answer is the best: http://stackoverflow.com/questions/20433867/git-ahead-behind-info-between-master-and-branch/27940027#27940027 (works with git 1.9) – jakub.g Mar 02 '16 at 19:57
  • As for the point 2 of your question: `How to get the numbers? How to know whether it is ahead/behind? By how many commits? And what about the diverged branch case?` This answer worked wonderfully for me: http://stackoverflow.com/a/27940027/4209853 – xDaizu Dec 13 '16 at 12:57

10 Answers10

23

git rev-list origin..HEAD will show the commits that are in your current branch, but not origin -- i.e., whether you're ahead of origin and by which commits.

git rev-list HEAD..origin will show the opposite.

If both commands show commits, then you have diverged branches.

jamessan
  • 37,987
  • 8
  • 79
  • 85
  • Thanks, the `rev-list` seems to work as expected, but `branch -r` does not give remote tracked branch accurately. I do not have the `origin/HEAD` that you wrote, yet there is a remote tracked branch, and I can get the number of commits with `rev-list`. – Olivier Verdier Jun 03 '10 at 20:22
  • Hmm, it does seem that `branch -r` doesn't do what I thought. Updated. – jamessan Jun 03 '10 at 21:42
18

update

As pointed out by amalloy, recent versions of git support finding the matching tracking branch for a given branch by giving "branchname@{upstream}" (or "branchname@{u}", or "@{u}" for the tracking branch of HEAD). This effectively supercedes the script below. You can do:

git rev-list @{u}..
git rev-list --left-right --boundary @{u}...
gitk @{u}...

etc. For example, I have git q aliased to git log --pretty='...' @{u}.. to show me "queued" commits ready for pushing.

original answer

There doesn't seem to be an easy way to find the tracking branch in general, without parsing lots more git config than is practical in a few shell commands. But for many cases this will go a long way:

# work out the current branch name
currentbranch=$(expr $(git symbolic-ref HEAD) : 'refs/heads/\(.*\)')
[ -n "$currentbranch" ] || die "You don't seem to be on a branch"
# look up this branch in the configuration
remote=$(git config branch.$currentbranch.remote)
remote_ref=$(git config branch.$currentbranch.merge)
# convert the remote ref into the tracking ref... this is a hack
remote_branch=$(expr $remote_ref : 'refs/heads/\(.*\)')
tracking_branch=refs/remotes/$remote/$remote_branch
# now $tracking_branch should be the local ref tracking HEAD
git rev-list $tracking_branch..HEAD

Another, more brute-force, approach:

git rev-list HEAD --not --remotes

jamessan's answer explains how to find the relative differences between $tracking_branch and HEAD using git rev-list. One fun thing you can do:

git rev-list --left-right $tracking_branch...HEAD

(note three dots between $tracking_branch and HEAD). This will show commits on both "arms" with a distinguishing mark at the front: "<" for commits on $tracking_branch, and ">" for commits on HEAD.

pradyunsg
  • 13,385
  • 10
  • 36
  • 80
araqnid
  • 108,587
  • 20
  • 147
  • 127
  • 1
    Ecxellent! The three dots with `--left-right` is definitely an improvement on jamessan's answer. For the remote, I suppose the only robust way to go is, as you suggest, to read the git config... – Olivier Verdier Jun 04 '10 at 06:54
  • Doesn't work for me - see [my answer below](http://stackoverflow.com/a/8294373/179332). – Adam Spiers Feb 11 '12 at 10:17
  • Isn't `@{u}` the current SHA of the branch you're tracking? I think your entire script could be replaced with `git rev-list @{u}..HEAD` in halfway-recent versions of git (ie, not four years old). – amalloy Feb 14 '14 at 21:29
  • @amalloy yes, that seems to be the case. I don't know when it was added, thanks for pointing it out! – araqnid Feb 17 '14 at 09:41
11

You can try git branch -v -v. With -v flag given twice, it outputs names of upstream branches. Sample output:

* devel  7a5ff2c [origin/devel: ahead 1] smaller file status overlay icons
  master 37ca389 [origin/master] initial project check-in.

I think this format is more stable than git status output.

max
  • 31,363
  • 7
  • 64
  • 81
  • 1
    `git status` has the `--porcelain` flag which provides stable output, but it doesn't show the ahead/behind information. – jamessan Jun 04 '10 at 03:54
  • excellent! I had never noticed this double option with git branch... it's pretty unusual to have a command with a double option, isn't it? Thanks for the tip – Olivier Verdier Jun 04 '10 at 06:40
  • 1
    This looks nice and simple. I will use this. – FractalSpace Nov 08 '12 at 14:18
  • @OlivierVerdier: Nah, that's not totally unheard of you. You've just probably seen it as `-vv` instead of `-v -v`. You see it occasionally to provide different levels of verbose output without adding distinct flags for each level. – Christopher Jun 14 '13 at 19:23
  • 1
    @jamessan - you can use `--porcelain --branch` to see the ahead/behind info (though you need a pretty recent version of git for that, it isn't in 1.7.9 (ubuntu 12.04) but is in 1.8.3). `--short --branch` is pretty much the same for now, and is available in 1.7.9 – Hamish Downer Aug 19 '13 at 14:21
  • @HamishDowner, me, personally, would prefer your suggestion as an accepted answer. – Dmitry Volosnykh Mar 25 '16 at 06:10
7

Edit: My original answer was actually not very good because it relied on the user to have a remote called "origin". It also failed if the current branch was had a tracking branch besides origin-head. These flaws essentially made it useless. However, the answer by @araqnid is not the most efficient method and the way he arrives at $tracking_branch is less than strait forward. The most efficient (fastest) method I have found to get the same functionality is the following:

# get the tracking-branch name
tracking_branch=$(git for-each-ref --format='%(upstream:short)' $(git symbolic-ref -q HEAD))
# creates global variables $1 and $2 based on left vs. right tracking
# inspired by @adam_spiers
set -- $(git rev-list --left-right --count $tracking_branch...HEAD)
behind=$1
ahead=$2

original answer: (inferior, but given for clarity)

Perhaps the simplest method I could find (inspired by @insidepower)

# count the number of logs
behind=$(git log --oneline HEAD..origin | wc -l)
ahead=$( git log --oneline origin..HEAD | wc -l)

I had previously been using the method of @araqnid, but now I think I'll move some of my scripts to this method since it is much simpler. This should work on any unix system.

scicalculator
  • 1,368
  • 3
  • 15
  • 33
  • When was the `--count` option to `got rev-list` added? It doesn't seem to exist in `git` 1.7.1 that I'm using. – Mark Booth Feb 28 '13 at 15:35
  • I'm not sure which version it was introduced in, but it has been there since I started using git over a year ago. This email on the mailing list "[PATCH rev-list --count: separate count for --cherry-mark](http://git.661346.n2.nabble.com/PATCH-rev-list-count-separate-count-for-cherry-mark-td6304977.html)" (v1.7.5) shows that it was available as far back as Apr 2011. – scicalculator Mar 02 '13 at 14:51
  • Sigh. Sadly one of the downsides of using a *supported* Linux like RHEL6 in an enterprise environment is having to struggle on with woefully old versions of some relatively fast moving software like `git`. Having done a little research, it looks like *lots* of useful functionality was added to git between 1.7.1 and 1.7.6. Unfortunately I can't find a page for `git` which is equivalent to the [WhatsNew](http://mercurial.selenic.com/wiki/WhatsNew) page for `hg`. – Mark Booth Mar 04 '13 at 11:42
  • Just to add to the original answer, you can get the name of the origin using: `remote="$(git config --get branch.${branch}.remote 2>/dev/null)"` then you can just use the version in the original answer as well. – Hosh Sadiq Aug 24 '13 at 09:28
6

In modern versions of git, @{u} points to the upstream of the current branch, if one is set.

So to count how many commits you are behind the remote tracking branch:

git rev-list HEAD..@{u} | wc -l

And to see how far you are ahead of the remote, just switch the order:

git rev-list @{u}..HEAD | wc -l

For a more human-readable summary, you could ask for a log instead:

git log --pretty=oneline @{u}..HEAD

For my own purposes, I am working on a script that will replace @{u} with an appropriate guess, if no upstream is yet set. Unfortunately there is at this time no @{d} to represent the downstream (where you would push to).

joeytwiddle
  • 24,338
  • 11
  • 107
  • 91
5

git status has a --porcelain option that is intended for parsing by scripts. It is based on the --short output - they are almost identical at the time of writing (see the "Porcelain Format" section of the git status man page for details). The main difference is that --short has colour output.

By default no branch information is shown, but if you add the --branch option you will get output like:

git status --short --branch
## master...origin/master [ahead 1]
?? untrackedfile.txt
...

If you are up to date (after a fetch), the branch line will just be:

## master

If you are ahead:

## master...origin/master [ahead 1]

If you are behind:

## master...origin/master [behind 58]

And for both:

## master...origin/master [ahead 1, behind 58]

Note that git status --porcelain --branch is only available in 1.7.10.3 or later (though git status --short --branch has been available since 1.7.2 ).

Hamish Downer
  • 15,325
  • 14
  • 82
  • 80
2

Why wouldn't this work:

#!/bin/sh
git diff origin/master..HEAD --quiet --exit-code
RETVAL=$?
if [ $RETVAL -gt 0 ]; then
    echo "You need to git push!"
else
    echo "No git push necessary!"
fi 
Till
  • 21,590
  • 4
  • 55
  • 86
  • This wouldn't work because I wanted to know by how much the repo is ahead/behind the remote. Anyway, this question has already a pretty good answer. – Olivier Verdier Jun 05 '12 at 07:53
  • I think for ahead, `RETVAL` would be the number of commits it is ahead. I am not sure if it works on 'behind' (e.g. _requires_ pull). – Till Jun 06 '12 at 13:01
1

How to know the remote tracked branch? It is often origin/branch but need not be.

Git 2.5+ introduces a new shortcut which references the branch you are pushing to. @{push}: that would be the remote tracking branch which is of interest here.

That means you have another option to see ahead/behind for all branches which are configured to push to a branch.

git for-each-ref --format="%(push:track)" refs/heads

See more at "Viewing Unpushed Git Commits"

Community
  • 1
  • 1
VonC
  • 1,042,979
  • 435
  • 3,649
  • 4,283
1

The top chunk of code in araqnid's answer doesn't work for me, so maybe something in git has changed since it was written 18 months ago. It works if I change:

tracking_branch=refs/remotes/$remote/$remote_branch

to

tracking_branch=$remote/$remote_branch

However there is still an issue when tracking a local branch, in which case you have to trim the remote part (which becomes '.'):

tracking_branch=${tracking_branch#./}

Then you can programmatically obtain the number of revisions behind and ahead as follows:

set -- `git rev-list --left-right --count $tracking_branch...HEAD`
behind="$1"
ahead="$2"

I've written scripts to do all that (and more - e.g. they can also attempt to spot remotes on the other side of a git-svn bridge), and published them in my git-config repository on github. For example, here's my git-compare-upstream. See the README for installation instructions and other handy related scripts.

Adam Spiers
  • 15,491
  • 5
  • 40
  • 61
0

With recent versions of git you should use

git status --porcelain --branch

and check for ahead/behind:

## master...origin/master [behind 1]

--porcelain[=version]

Give the output in an easy-to-parse format for scripts. This is similar to the short output, but will remain stable across Git versions and regardless of user configuration. See below for details.

-b, --branch

Show the branch and tracking info even in short-format.

laktak
  • 47,916
  • 15
  • 112
  • 150