61

How can I find the number of commits between two commitishes in git?

Additionally, is there some way that I could do the same with any project on GitHub (using the UI, not the API)?

haneefmubarak
  • 1,911
  • 1
  • 21
  • 32
  • If you have two commits you have their revision number no? Subtract the two? I may be oversimplifying or not understanding the question. – Carlos Bribiescas Aug 13 '15 at 20:40
  • 1
    @CarlosBribiescas in `git`, a commitish isn't necessarily a commit ID. Also, even if you have the commit IDs, in git, commit IDs are hashes (ff3823ac, 554fbae3, etc.). – haneefmubarak Aug 13 '15 at 20:42

4 Answers4

63

Before I give you an answer, consider this commit graph:

        o -----------
       /             \
... - A - o - o - o - B
       \         /
        o ----- o

Each o represents a commit, as do A and B (they're just letters to let us talk about specific commits). How many commits are there between commits A and B?

That said, in more linear cases, just use git rev-list --count A..B and then decide what you mean by "between" (does it include B and exclude A? that's how git rev-list --count will behave). In branchy cases like this, you'll get all the commits down all the branches; add --first-parent, for instance, to follow just the "main line".

(You also mentioned "commitish", suggesting that we might have annotated tags. That won't affect the output from git rev-list, which only counts specific commits.)


Edit: Since git rev-list --count A..B includes commit B (while omitting commit A), and you want to exclude both end-points, you need to subtract one. In modern shells you can do this with shell arithmetic:

count=$(($(git rev-list --count A..B) - 1))

For instance:

$ x=$(($(git rev-list --count HEAD~3..HEAD) - 1))
$ echo $x
2

(this particular repo has a very linear graph structure, so there are no branches here and there are two commits "between" the tip and three-behind-the-tip). Note, however, that this will produce -1 if A and B identify the same commit:

$ x=$(($(git rev-list --count HEAD..HEAD) - 1))
$ echo $x
-1

so you might want to check that first:

count=$(git rev-list --count $start..$end)
if [ $count -eq 0 ]; then
    ... possible error: start and end are the same commit ...
else
    count=$((count - 1))
fi
torek
  • 448,244
  • 59
  • 642
  • 775
  • What do you do if you want to get the total across all of the branches added up? Also, by between, I was thinking of "exclusive" (excludes A & B). – haneefmubarak Aug 13 '15 at 20:49
  • The `git rev-list` command (try it without `--count`) walks the graph, printing the SHA-1 of every commit you select. The `A..B` notation means "select every commit reachable starting from B and working back through all parent commits, but then exclude every commit reachable by starting from A and working back", so if you want all of them, you're in great shape, because that's what you get. Meanwhile, since `rev-list` includes `A` itself, subtract one. – torek Aug 13 '15 at 20:53
  • could you put the comment in your answer along with a one liner that also subtracts one (perhaps pipe the result into `| xargs expr -1 +`) and then I'll mark it as correct – haneefmubarak Aug 13 '15 at 20:57
  • Done. Also I fixed the remark about which commit is included in the revisions walked (it's `B`, not `A`, that gets counted). – torek Aug 14 '15 at 00:54
  • Does this guarantee that `A` is a parent of `B`? What if `A` is in another branch, not merged yet... or a descendant of `B`? – aross May 22 '23 at 10:09
50
$ git log 375a1..58b20 --pretty=oneline | wc -l

Specify your start commit followed by your end commit, and then count the lines. That should be the count of commits between those two commit ranges. Use the --pretty=oneline formatting so that each commit takes up a single line.

Note that using two dots (375a1..58b20) is different than using three dots (375a1...58b20); see What are the differences between double-dot “..” and triple-dot “…” in Git commit ranges? for more information about this and to figure out which one you want to use.

As for the GUI in GitHub, I don't know of a way to accomplish this same task. But that should be trivial, as the above is the possible way to do it directly using Git and Bash.

Camelid
  • 1,535
  • 8
  • 21
Thomas Stringer
  • 5,682
  • 3
  • 24
  • 40
  • 1
    It requires two dots or three dots between the commit id's in the above command? – a.saurabh Sep 12 '18 at 12:24
  • 1
    This was probably just copied over from here: http://gal.steinitz.com/blog/2013/07/27/git-tips-count-number-of-commits-between-two-commits/ – kghbln Dec 09 '18 at 11:01
  • 1
    it's worth reading the comments to @kghbln's comment link for details about order of first and second commits, and an option that doesn't rely on `wc` (namely `git rev-list [newer] ^[older] --count`) – matt wilkie May 04 '19 at 23:53
  • With two dots, I get nothing. With three dots, it works. – Abhijit Sarkar Oct 22 '20 at 08:41
7

Another ONE LINER

git rev-list newer ^older --pretty=oneline --count

Use revision numbers or SHAs:

git rev-list db8fb95e6256bd52a668bae82d8b5a73152869fa ^1aeae117c58c173fee9cb3550297498142887aa5 --pretty=oneline --count
  • [newer] and [older] can be SHA’s, branches or tags.
  • Important: If you have a complicated git graph you should read @torek 's excellent answer.
  • Credit goes to @matt wilkie in his comment and the original source.
EliteRaceElephant
  • 7,744
  • 4
  • 47
  • 62
0

commit ranges can fail on a broken repo:

$ git rev-list --count b00aa8ded74..master
error: Could not read 29026cc404895cc9f9afa55c4e2d53b7a4a5a319
fatal: revision walk setup failed

$ git log --oneline b00aa8ded74..master | wc -l
error: Could not read 29026cc404895cc9f9afa55c4e2d53b7a4a5a319
fatal: revision walk setup failed

alternative: git log and grep

depth=$(git log --format=%H $rev2 | grep -m1 -n -x $rev1 | cut -d: -f1)

grep -m1 will stop git log after the first match, so there is no error

milahu
  • 2,447
  • 1
  • 18
  • 25