diff --git a/CHANGELOG b/CHANGELOG
index 999b21f758a6d445ed05df7425ae19aed35a4570..8914068570eb109e8b60d7fdb82e99c35dc19e73 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -29,7 +29,7 @@ v 7.8.0
   - 
   - 
   - 
-  - 
+  - Add a commit calendar to the user profile (Hannes Rosenögger)  
   - 
   - 
   - 
diff --git a/Gemfile b/Gemfile
index 96a1097d6d856d1bf19eac9138bd7126670682b7..6e4e20f5e1f364a1853c03de4500ca37a8896f81 100644
--- a/Gemfile
+++ b/Gemfile
@@ -154,6 +154,9 @@ gem "slack-notifier", "~> 1.0.0"
 # d3
 gem "d3_rails", "~> 3.1.4"
 
+#cal-heatmap
+gem "cal-heatmap-rails", "~> 0.0.1"
+
 # underscore-rails
 gem "underscore-rails", "~> 1.4.4"
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 18fae9b70015aab6df5a688929fb2028dba35072..5c70541a73523f9109c315bcbce8abe4a6afcbeb 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -52,6 +52,7 @@ GEM
       sass (~> 3.2)
     browser (0.7.2)
     builder (3.2.2)
+    cal-heatmap-rails (0.0.1)
     capybara (2.2.1)
       mime-types (>= 1.16)
       nokogiri (>= 1.3.3)
@@ -627,6 +628,7 @@ DEPENDENCIES
   binding_of_caller
   bootstrap-sass (~> 3.0)
   browser
+  cal-heatmap-rails (~> 0.0.1)
   capybara (~> 2.2.1)
   carrierwave
   coffee-rails
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 337170605dc9bc72719b3803d87e8954828e0288..4912c534b0ee8a6cd7cfc4d887db45abc4b911bc 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -39,6 +39,7 @@
 #= require shortcuts_dashboard_navigation
 #= require shortcuts_issueable
 #= require shortcuts_network
+#= require cal-heatmap
 #= require_tree .
 
 window.slugify = (text) ->
diff --git a/app/assets/javascripts/calendar.js.coffee b/app/assets/javascripts/calendar.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..e3bb420a2785d5d7e23ce6b9af108265823874c1
--- /dev/null
+++ b/app/assets/javascripts/calendar.js.coffee
@@ -0,0 +1,71 @@
+class @calendar
+  options =
+    month: "short"
+    day: "numeric"
+    year: "numeric"
+
+  constructor: (timestamps,starting_year,starting_month,activities_path) ->
+    cal = new CalHeatMap()
+    cal.init
+      itemName: ["commit"]
+      data: timestamps
+      start: new Date(starting_year, starting_month)
+      domainLabelFormat: "%b"
+      id: "cal-heatmap"
+      domain: "month"
+      subDomain: "day"
+      range: 12
+      tooltip: true
+      domainDynamicDimension: false
+      colLimit: 4
+      label:
+        position: "top"
+      domainMargin: 1
+      legend: [
+        0
+        1
+        4
+        7
+      ]
+      legendCellPadding: 3
+      onClick: (date, count) ->
+        $.ajax
+          url: activities_path
+          data:
+            date: date
+
+          dataType: "json"
+          success: (data) ->
+            $("#loading_commits").fadeIn()
+            calendar.calendarOnClick data, date, count
+            setTimeout (->
+              $("#calendar_onclick_placeholder").fadeIn 500
+              return
+            ), 400
+            setTimeout (->
+              $("#loading_commits").hide()
+              return
+            ), 400
+            return  
+        return
+    return
+
+  @calendarOnClick: (data, date, nb)->
+    $("#calendar_onclick_placeholder").hide()
+    $("#calendar_onclick_placeholder").html ->
+      "<span class='calendar_onclick_second'><b>" +
+      ((if nb is null then "no" else nb)) + 
+      "</b><span class='calendar_commit_date'> commit" + 
+      ((if (nb isnt 1) then "s" else "")) + " " + 
+      date.toLocaleDateString("en-US", options) + 
+      "</span><hr class='calendar_onclick_hr'></span>"
+    $.each data, (key, data) ->
+      $.each data, (index, data) ->
+        $("#calendar_onclick_placeholder").append ->
+          "Pushed <b>" + ((if data is null then "no" else data)) + " commit" +
+          ((if (data isnt 1) then "s" else "")) + 
+          "</b> to <a href='/" + index + "'>" + 
+          index + "</a><hr class='calendar_onclick_hr'>"
+        return
+      return
+    return
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index 3cf08782c3c2add3ad4d9dd3dd132eac4549b754..8f63a7fee648ad5f6099b85ffa1b94e449f74f4f 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -8,6 +8,7 @@
  *= require select2
  *= require_self
  *= require dropzone/basic
+ *= require cal-heatmap
 */
 
 @import "main/*";
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/generic/calendar.scss
new file mode 100644
index 0000000000000000000000000000000000000000..9483b26164e07f1f940181e977fbd31619cecbdd
--- /dev/null
+++ b/app/assets/stylesheets/generic/calendar.scss
@@ -0,0 +1,95 @@
+.calendar_onclick_placeholder {
+  padding: 0 0 2px 0;
+}
+
+.calendar_commit_activity {
+  padding: 5px 0 0;
+}
+
+.calendar_onclick_second {
+  font-size: 14px;
+  display: block;
+}
+
+.calendar_onclick_hr {
+  padding: 0;
+  margin: 10px 0;
+}
+
+.calendar_commit_date {
+  color: #999;
+}
+
+.calendar_activity_summary {
+  font-size: 14px;
+}
+
+/**
+* This overwrites the default values of the cal-heatmap gem
+*/
+.calendar {
+  .qi {
+    background-color: #999;
+    fill: #fff;
+  }
+
+  .q1 {
+    background-color: #dae289;
+    fill: #ededed;
+  }
+
+  .q2 {
+    background-color: #cedb9c;
+    fill: #ACD5F2;
+  }
+
+  .q3 {
+    background-color: #b5cf6b;
+    fill: #7FA8D1;
+  }
+
+  .q4 {
+    background-color: #637939;
+    fill: #49729B;
+  }
+
+  .q5 {
+    background-color: #3b6427;
+    fill: #254E77;
+  }
+
+  .domain-background {
+    fill: none;
+    shape-rendering: crispedges;
+  }
+
+  .ch-tooltip {
+    position: absolute;
+    display: none;
+    margin-top: 22px;
+    margin-left: 1px;
+    font-size: 13px;
+    padding: 3px;
+    font-weight: 550;
+    background-color: #222;
+    span {
+      position: absolute;
+      width: 200px;
+      text-align: center;
+      visibility: hidden;
+      border-radius: 10px;
+      &:after {
+        content: '';
+        position: absolute;
+        top: 100%;
+        left: 50%;
+        margin-left: -8px;
+        width: 0;
+        height: 0;
+        border-top: 8px solid #000000;
+        border-right: 8px solid transparent;
+        border-left: 8px solid transparent;
+      }
+    }
+  }
+}
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 67af1801bda3192d40cb2d89252dc4e62fc348d1..a5e80f7e0089f1028159de6ba66f25aaf3bd89f6 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,5 +1,5 @@
 class UsersController < ApplicationController
-  skip_before_filter :authenticate_user!, only: [:show]
+  skip_before_filter :authenticate_user!, only: [:show, :activities]
   layout :determine_layout
 
   def show
@@ -10,7 +10,8 @@ class UsersController < ApplicationController
     end
 
     # Projects user can view
-    authorized_projects_ids = ProjectsFinder.new.execute(current_user).pluck(:id)
+    visible_projects = ProjectsFinder.new.execute(current_user)
+    authorized_projects_ids = visible_projects.pluck(:id)
 
     @projects = @user.personal_projects.
       where(id: authorized_projects_ids)
@@ -24,12 +25,32 @@ class UsersController < ApplicationController
 
     @title = @user.name
 
+    user_repositories = visible_projects.map(&:repository)
+    @timestamps = Gitlab::CommitsCalendar.create_timestamp(user_repositories,
+                                                           @user, false)
+    @starting_year = Gitlab::CommitsCalendar.starting_year(@timestamps)
+    @starting_month = Gitlab::CommitsCalendar.starting_month(@timestamps)
+    @last_commit_date = Gitlab::CommitsCalendar.last_commit_date(@timestamps)
+
     respond_to do |format|
       format.html
       format.atom { render layout: false }
     end
   end
 
+  def activities
+    user = User.find_by_username!(params[:username])
+    # Projects user can view
+    visible_projects = ProjectsFinder.new.execute(current_user)
+
+    user_repositories = visible_projects.map(&:repository)
+    user_activities = Gitlab::CommitsCalendar.create_timestamp(user_repositories,
+                                                               user, true)
+    user_activities = Gitlab::CommitsCalendar.commit_activity_match(
+                                              user_activities, params[:date])
+    render json: user_activities.to_json
+  end
+
   def determine_layout
     if current_user
       'navless'
diff --git a/app/models/repository.rb b/app/models/repository.rb
index e93c76790c7dcf56ba335c816bc04889a3e7a37e..e44ecca865c9fbcc352caeb763ce8622ee0c5381 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -139,21 +139,46 @@ class Repository
 
   def graph_log
     Rails.cache.fetch(cache_key(:graph_log)) do
+
+      # handle empty repos that don't have a root_ref set yet
+      unless raw_repository.root_ref.present?
+        raw_repository.root_ref = 'refs/heads/master'
+      end
+
       commits = raw_repository.log(limit: 6000, skip_merges: true,
-                                   ref: root_ref)
+                                   ref: raw_repository.root_ref)
+
       commits.map do |rugged_commit|
-        commit = Gitlab::Git::Commit.new(rugged_commit)
 
+        commit = Gitlab::Git::Commit.new(rugged_commit)
         {
           author_name: commit.author_name.force_encoding('UTF-8'),
           author_email: commit.author_email.force_encoding('UTF-8'),
           additions: commit.stats.additions,
-          deletions: commit.stats.deletions
+          deletions: commit.stats.deletions,
+          date: commit.committed_date
         }
       end
     end
   end
 
+  def graph_logs_by_user_email(user)
+    graph_log.select { |u_email| u_email[:author_email] == user.email }
+  end
+
+  def timestamps_by_user_from_graph_log(user)
+    graph_logs_by_user_email(user).map { |graph_log| graph_log[:date].to_time.to_i }
+  end
+
+  def commits_log_of_user_by_date(user)
+    timestamps_by_user_from_graph_log(user).
+      group_by { |commit_date| commit_date }.
+      inject({}) do |hash, (timestamp_date, commits)|
+        hash[timestamp_date] = commits.count
+        hash
+      end
+  end
+
   def cache_key(type)
     "#{type}:#{path_with_namespace}"
   end
diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml
index 613833153739e0afb591217d189971b424932473..c7976ba564fe2657de39ff41a99e9eaa6aa4d0a6 100644
--- a/app/views/events/_event.html.haml
+++ b/app/views/events/_event.html.haml
@@ -11,5 +11,4 @@
       - elsif event.note?
         = render "events/event/note", event: event
       - else
-        = render "events/event/common", event: event
-
+        = render "events/event/common", event: event
\ No newline at end of file
diff --git a/app/views/users/_calendar.html.haml b/app/views/users/_calendar.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..70d5cca854d4594bdbc31df0314b3a11d9f8c9d0
--- /dev/null
+++ b/app/views/users/_calendar.html.haml
@@ -0,0 +1,9 @@
+#cal-heatmap.calendar
+  :javascript 
+    new calendar(
+      #{@timestamps.to_json},
+      #{@starting_year},
+      #{@starting_month},
+      '#{user_activities_path}'
+    );
+= render "calendar_onclick"
diff --git a/app/views/users/_calendar_onclick.html.haml b/app/views/users/_calendar_onclick.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..1514b56bb23b453f869020441aa473f0c0f616cf
--- /dev/null
+++ b/app/views/users/_calendar_onclick.html.haml
@@ -0,0 +1,25 @@
+#calendar_commit_activity.calendar_commit_activity
+  %h4.activity_title Commit Activity:
+
+  #loading_commits
+    %section.text-center
+      %h3
+        %i.icon-spinner.icon-spin
+
+  #calendar_onclick_placeholder.calendar_onclick_placeholder
+    %span.calendar_onclick_second.calendar_onclick_second
+    - if @timestamps.empty?
+      %span.calendar_activity_summary
+        %strong> #{@user.username}
+        &nbsp; has no activity
+    - else
+      %span.calendar_activity_summary
+        %strong> #{@user.username}
+        's last commit was on
+        %span.commit_date #{@last_commit_date}
+
+    %hr.calendar_onclick_hr
+
+:javascript
+  $("#loading_commits").hide();
+  
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 54f2666ce5d9c14ca053387718ea5a33742c0a73..0d214d31607a5a2d63e702bfb560b85f9907231b 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -18,6 +18,8 @@
       %h4 Groups:
       = render 'groups', groups: @groups
       %hr
+    %h4 Calendar:
+    = render 'calendar'
     %h4
       User Activity:
 
diff --git a/config/routes.rb b/config/routes.rb
index f29b620e079af1b5a9e343daac36a35a1f40186c..5d61de29b9ab3350b162a77e692b8ad868b5bc1f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -157,6 +157,10 @@ Gitlab::Application.routes.draw do
     end
   end
 
+  # route for commits used by the cal-heatmap
+  get 'u/:username/activities' => 'users#activities', as: :user_activities,
+      constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ },
+      via: :get
   get '/u/:username' => 'users#show', as: :user,
       constraints: { username: /(?:[^.]|\.(?!atom$))+/, format: /atom/ }
 
diff --git a/lib/gitlab/commits_calendar.rb b/lib/gitlab/commits_calendar.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a862e67a5989836bd33f88db9e48ebd5e5a3953a
--- /dev/null
+++ b/lib/gitlab/commits_calendar.rb
@@ -0,0 +1,79 @@
+module Gitlab
+  class CommitsCalendar
+    def self.create_timestamp(repositories, user, show_activity)
+      timestamps = {}
+      repositories.each do |raw_repository|
+        if raw_repository.exists?
+          commits_log = raw_repository.commits_log_of_user_by_date(user)
+
+          populated_timestamps =
+            if show_activity
+              populate_timestamps_by_project(
+                commits_log,
+                timestamps,
+                raw_repository
+              )
+            else
+              populate_timestamps(commits_log, timestamps)
+            end
+          timestamps.merge!(populated_timestamps)
+        end
+      end
+      timestamps
+    end
+
+    def self.populate_timestamps(commits_log, timestamps)
+      commits_log.each do |timestamp_date, commits_count|
+        hash = { "#{timestamp_date}" => commits_count }
+        if timestamps.has_key?("#{timestamp_date}")
+          timestamps.merge!(hash) do |timestamp_date, commits_count,
+            new_commits_count| commits_count = commits_count.to_i +
+            new_commits_count
+          end
+        else
+          timestamps.merge!(hash)
+        end
+      end
+      timestamps
+    end
+
+    def self.populate_timestamps_by_project(commits_log, timestamps,
+                                            project)
+      commits_log.each do |timestamp_date, commits_count|
+        if timestamps.has_key?("#{timestamp_date}")
+          timestamps["#{timestamp_date}"].
+            merge!(project.path_with_namespace => commits_count)
+        else
+          hash = { "#{timestamp_date}" => { project.path_with_namespace =>
+                                            commits_count } }
+          timestamps.merge!(hash)
+        end
+      end
+      timestamps
+    end
+
+    def self.latest_commit_date(timestamps)
+      if timestamps.nil? || timestamps.empty?
+        DateTime.now.to_date
+      else
+        Time.at(timestamps.keys.first.to_i).to_date
+      end
+    end
+
+    def self.starting_year(timestamps)
+       DateTime.now.to_date - 1
+    end
+
+    def self.starting_month(timestamps)
+       Date.today.strftime("%m").to_i
+    end
+
+    def self.last_commit_date(timestamps)
+      latest_commit_date(timestamps).to_formatted_s(:long).to_s
+    end
+
+    def self.commit_activity_match(user_activities, date)
+      user_activities.select { |x| Time.at(x.to_i) == Time.parse(date) }
+    end
+  end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bfbe5254bbeff4860bc05c0edad46285379d4dc1
--- /dev/null
+++ b/spec/controllers/users_controller_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe UsersController do
+  let(:user)    { create(:user, username: "user1", name: "User 1", email: "user1@gitlab.com") }
+  
+  before do
+    sign_in(user)
+  end
+
+  describe "GET #show" do 
+    render_views
+    before do
+      get :show, username: user.username
+    end
+
+    it "renders the show template" do
+      expect(response.status).to eq(200)
+      expect(response).to render_template("show")
+    end
+
+    it "renders calendar" do
+      controller.prepend_view_path 'app/views/users'
+      expect(response).to render_template("_calendar")
+    end
+  end
+end
+