diff --git a/CHANGELOG b/CHANGELOG
index 0a0afb4a0538aadd144c01827d08ee0d7f2abbb8..b59d4b39e5a30e9593c00b1bf3733a153d96e3c4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -16,6 +16,7 @@ v 8.1.0 (unreleased)
   - Move CI charts to project graphs area
   - Fix cases where Markdown did not render links in activity feed (Stan Hu)
   - Add first and last to pagination (Zeger-Jan van de Weg)
+  - Added Commit Status API
   - Show CI status on commit page
   - Show CI status on Your projects page and Starred projects page
   - Remove "Continuous Integration" page from dashboard
@@ -46,6 +47,11 @@ v 8.1.0 (unreleased)
   - Fix bug where Emojis in Markdown would truncate remaining text (Sakata Sinji)
   - Persist filters when sorting on admin user page (Jerry Lukins)
   - Allow dashboard and group issues/MRs to be filtered by label
+  - Add spellcheck=false to certain input fields
+  - Invalidate stored service password if the endpoint URL is changed
+  - Project names are not fully shown if group name is too big, even on group page view
+  - Apply new design for Files page
+  - Add "New Page" button to Wiki Pages tab (Stan Hu)
 
 v 8.0.4
   - Fix Message-ID header to be RFC 2111-compliant to prevent e-mails being dropped (Stan Hu)
@@ -60,6 +66,7 @@ v 8.0.3
   - Fix URL shown in Slack notifications
   - Fix bug where projects would appear to be stuck in the forked import state (Stan Hu)
   - Fix Error 500 in creating merge requests with > 1000 diffs (Stan Hu)
+  - Add work_in_progress key to MR web hooks (Ben Boeckel)
 
 v 8.0.2
   - Fix default avatar not rendering in network graph (Stan Hu)
diff --git a/Gemfile b/Gemfile
index e8cdb7a4a9790d4df74c2add5fab5a0862e3dc19..967092994a670451318a7ea6e1b4540d16b25d03 100644
--- a/Gemfile
+++ b/Gemfile
@@ -290,7 +290,7 @@ gem 'newrelic-grape'
 
 gem 'octokit', '~> 3.7.0'
 
-gem "mail_room", "~> 0.5.2"
+gem "mail_room", "~> 0.6.0"
 
 gem 'email_reply_parser', '~> 0.5.8'
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 9911904d304129bcabd657690a471d7f502d9d9b..58426a606836a12140e7e52a36573accf8fbdb4d 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -392,7 +392,7 @@ GEM
       systemu (~> 2.6.2)
     mail (2.6.3)
       mime-types (>= 1.16, < 3)
-    mail_room (0.5.2)
+    mail_room (0.6.0)
     method_source (0.8.2)
     mime-types (1.25.1)
     mimemagic (0.3.0)
@@ -854,7 +854,7 @@ DEPENDENCIES
   jquery-ui-rails (~> 4.2.1)
   kaminari (~> 0.16.3)
   letter_opener (~> 1.1.2)
-  mail_room (~> 0.5.2)
+  mail_room (~> 0.6.0)
   minitest (~> 5.7.0)
   mousetrap-rails (~> 1.4.6)
   mysql2 (~> 0.3.16)
diff --git a/app/assets/javascripts/line_highlighter.js.coffee b/app/assets/javascripts/line_highlighter.js.coffee
index e604e6025c2fb0caf51f826c08493126f3a2f268..2254a3f91aeb35dc08e06d43aea64a129d8598a2 100644
--- a/app/assets/javascripts/line_highlighter.js.coffee
+++ b/app/assets/javascripts/line_highlighter.js.coffee
@@ -6,7 +6,7 @@
 #
 # ### Example Markup
 #
-#   <div id="tree-content-holder">
+#   <div id="blob-content-holder">
 #     <div class="file-content">
 #       <div class="line-numbers">
 #         <a href="#L1" id="L1" data-line-number="1">1</a>
@@ -53,7 +53,7 @@ class @LineHighlighter
         $.scrollTo("#L#{range[0]}", offset: -150)
 
   bindEvents: ->
-    $('#tree-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
+    $('#blob-content-holder').on 'mousedown', 'a[data-line-number]', @clickHandler
 
     # While it may seem odd to bind to the mousedown event and then throw away
     # the click event, there is a method to our madness.
@@ -62,7 +62,7 @@ class @LineHighlighter
     # active state even when the event is cancelled, resulting in an ugly border
     # around the link and/or a persisted underline text decoration.
 
-    $('#tree-content-holder').on 'click', 'a[data-line-number]', (event) ->
+    $('#blob-content-holder').on 'click', 'a[data-line-number]', (event) ->
       event.preventDefault()
 
   clickHandler: (event) =>
diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss
index d9ede6379440525a0d6935209cb58a5841c6db56..7b060ce48532c36dadd52950c5f8f293a7515821 100644
--- a/app/assets/stylesheets/application.scss
+++ b/app/assets/stylesheets/application.scss
@@ -11,59 +11,41 @@
  *= require cal-heatmap
 */
 
+/*
+ * Welcome to GitLab css!
+ * If you need to add or modify UI component that is common for many pages
+ * like a table or typography then make changes in the framework/ directory.
+ * If you need to add unique style that should affect only one page - use pages/
+ * directory.
+ */
 
-@import "base/fonts";
-@import "base/variables";
-@import "base/mixins";
-@import "base/layout";
-
-
-/**
- * Customized Twitter bootstrap
+/*
+ * GitLab UI framework
  */
-@import 'base/gl_variables';
-@import 'base/gl_bootstrap';
+@import "framework";
 
-/**
+/*
  * NProgress load bar css
  */
 @import 'nprogress';
 @import 'nprogress-bootstrap';
 
-/**
+/*
  * Font icons
- *
  */
 @import "font-awesome";
 
-/**
- * UI themes:
- */
-@import "themes/**/*";
-
-/**
- * Generic css (forms, nav etc):
- */
-@import "generic/**/*";
-
-/**
+/*
  * Page specific styles (issues, projects etc):
  */
-
 @import "pages/**/*";
 
-/**
+/*
  * Code highlight
  */
 @import "highlight/**/*";
 
-/**
+/*
  * Styles for JS behaviors.
  */
-@import "behaviors.scss";
-
-/**
- * CI specific styles:
- */
-@import "ci/**/*";
-
+@import "behaviors.scss";
\ No newline at end of file
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
new file mode 100644
index 0000000000000000000000000000000000000000..1ec9d2fd84f1fe0e5c69104c7a33c7564f085458
--- /dev/null
+++ b/app/assets/stylesheets/framework.scss
@@ -0,0 +1,33 @@
+@import "framework/fonts";
+@import "framework/variables";
+@import "framework/mixins";
+@import "framework/layout";
+@import 'framework/tw_bootstrap_variables';
+@import 'framework/tw_bootstrap';
+
+@import "framework/avatar.scss";
+@import "framework/blocks.scss";
+@import "framework/buttons.scss";
+@import "framework/calendar.scss";
+@import "framework/callout.scss";
+@import "framework/common.scss";
+@import "framework/files.scss";
+@import "framework/filters.scss";
+@import "framework/flash.scss";
+@import "framework/forms.scss";
+@import "framework/gfm.scss";
+@import "framework/gitlab-theme.scss";
+@import "framework/header.scss";
+@import "framework/highlight.scss";
+@import "framework/issue_box.scss";
+@import "framework/jquery.scss";
+@import "framework/lists.scss";
+@import "framework/markdown_area.scss";
+@import "framework/mobile.scss";
+@import "framework/pagination.scss";
+@import "framework/selects.scss";
+@import "framework/sidebar.scss";
+@import "framework/tables.scss";
+@import "framework/timeline.scss";
+@import "framework/typography.scss";
+@import "framework/zen.scss";
diff --git a/app/assets/stylesheets/generic/avatar.scss b/app/assets/stylesheets/framework/avatar.scss
similarity index 100%
rename from app/assets/stylesheets/generic/avatar.scss
rename to app/assets/stylesheets/framework/avatar.scss
diff --git a/app/assets/stylesheets/generic/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
similarity index 100%
rename from app/assets/stylesheets/generic/blocks.scss
rename to app/assets/stylesheets/framework/blocks.scss
diff --git a/app/assets/stylesheets/generic/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
similarity index 96%
rename from app/assets/stylesheets/generic/buttons.scss
rename to app/assets/stylesheets/framework/buttons.scss
index 11acbe3adfa98a102a5eff6a6fc12a859105b49c..e5f0c0ad9eff29b73dd03d7bc005e03e760676db 100644
--- a/app/assets/stylesheets/generic/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -6,7 +6,7 @@
   font-size: 13px;
   font-weight: 600;
   line-height: 18px;
-  padding: 11px 16px;
+  padding: 11px $gl-padding;
   letter-spacing: .4px;
 
   &:focus,
@@ -71,6 +71,14 @@
   @include btn-default;
   @include btn-white;
 
+  &.btn-sm {
+    padding: 5px 10px;
+  }
+
+  &.btn-xs {
+    padding: 1px 5px;
+  }
+
   &.btn-success,
   &.btn-new,
   &.btn-create,
diff --git a/app/assets/stylesheets/generic/calendar.scss b/app/assets/stylesheets/framework/calendar.scss
similarity index 100%
rename from app/assets/stylesheets/generic/calendar.scss
rename to app/assets/stylesheets/framework/calendar.scss
diff --git a/app/assets/stylesheets/generic/callout.scss b/app/assets/stylesheets/framework/callout.scss
similarity index 100%
rename from app/assets/stylesheets/generic/callout.scss
rename to app/assets/stylesheets/framework/callout.scss
diff --git a/app/assets/stylesheets/generic/common.scss b/app/assets/stylesheets/framework/common.scss
similarity index 99%
rename from app/assets/stylesheets/generic/common.scss
rename to app/assets/stylesheets/framework/common.scss
index 03919f15f1fc3f9525f3fa95b11b04b42ade58ec..e1a1793be9c1564e787fd3c1c2bd0876d2cf6626 100644
--- a/app/assets/stylesheets/generic/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -398,7 +398,3 @@ table {
 .space-right {
   margin-right: 10px;
 }
-
-.in-line {
-  display: inline-block;
-}
diff --git a/app/assets/stylesheets/generic/files.scss b/app/assets/stylesheets/framework/files.scss
similarity index 100%
rename from app/assets/stylesheets/generic/files.scss
rename to app/assets/stylesheets/framework/files.scss
diff --git a/app/assets/stylesheets/generic/filters.scss b/app/assets/stylesheets/framework/filters.scss
similarity index 100%
rename from app/assets/stylesheets/generic/filters.scss
rename to app/assets/stylesheets/framework/filters.scss
diff --git a/app/assets/stylesheets/generic/flash.scss b/app/assets/stylesheets/framework/flash.scss
similarity index 100%
rename from app/assets/stylesheets/generic/flash.scss
rename to app/assets/stylesheets/framework/flash.scss
diff --git a/app/assets/stylesheets/base/fonts.scss b/app/assets/stylesheets/framework/fonts.scss
similarity index 100%
rename from app/assets/stylesheets/base/fonts.scss
rename to app/assets/stylesheets/framework/fonts.scss
diff --git a/app/assets/stylesheets/generic/forms.scss b/app/assets/stylesheets/framework/forms.scss
similarity index 100%
rename from app/assets/stylesheets/generic/forms.scss
rename to app/assets/stylesheets/framework/forms.scss
diff --git a/app/assets/stylesheets/generic/gfm.scss b/app/assets/stylesheets/framework/gfm.scss
similarity index 95%
rename from app/assets/stylesheets/generic/gfm.scss
rename to app/assets/stylesheets/framework/gfm.scss
index bd9200ace23a84ed73376793970d34d7a0e20d6e..5ae0520fd7b6d9eabe815bef2bfbc80be70b6227 100644
--- a/app/assets/stylesheets/generic/gfm.scss
+++ b/app/assets/stylesheets/framework/gfm.scss
@@ -22,4 +22,5 @@
 
 .gfm-commit, .gfm-commit_range {
   font-family: $monospace_font;
+  font-size: 90%;
 }
diff --git a/app/assets/stylesheets/themes/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss
similarity index 100%
rename from app/assets/stylesheets/themes/gitlab-theme.scss
rename to app/assets/stylesheets/framework/gitlab-theme.scss
diff --git a/app/assets/stylesheets/generic/header.scss b/app/assets/stylesheets/framework/header.scss
similarity index 100%
rename from app/assets/stylesheets/generic/header.scss
rename to app/assets/stylesheets/framework/header.scss
diff --git a/app/assets/stylesheets/generic/highlight.scss b/app/assets/stylesheets/framework/highlight.scss
similarity index 100%
rename from app/assets/stylesheets/generic/highlight.scss
rename to app/assets/stylesheets/framework/highlight.scss
diff --git a/app/assets/stylesheets/generic/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
similarity index 100%
rename from app/assets/stylesheets/generic/issue_box.scss
rename to app/assets/stylesheets/framework/issue_box.scss
diff --git a/app/assets/stylesheets/generic/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
similarity index 100%
rename from app/assets/stylesheets/generic/jquery.scss
rename to app/assets/stylesheets/framework/jquery.scss
diff --git a/app/assets/stylesheets/base/layout.scss b/app/assets/stylesheets/framework/layout.scss
similarity index 100%
rename from app/assets/stylesheets/base/layout.scss
rename to app/assets/stylesheets/framework/layout.scss
diff --git a/app/assets/stylesheets/generic/lists.scss b/app/assets/stylesheets/framework/lists.scss
similarity index 95%
rename from app/assets/stylesheets/generic/lists.scss
rename to app/assets/stylesheets/framework/lists.scss
index 3bfed8de7720d9d0a0fa9de37d17c93a1d0863ba..c5764c36597a9a4276c4d83f9b06cd548a09b338 100644
--- a/app/assets/stylesheets/generic/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -117,8 +117,12 @@ ul.content-list {
     }
 
     .controls {
-      padding-top: 10px;
+      padding-top: 4px;
       float: right;
+
+      .btn {
+        padding: 10px 14px;
+      }
     }
   }
 }
diff --git a/app/assets/stylesheets/generic/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
similarity index 100%
rename from app/assets/stylesheets/generic/markdown_area.scss
rename to app/assets/stylesheets/framework/markdown_area.scss
diff --git a/app/assets/stylesheets/base/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
similarity index 55%
rename from app/assets/stylesheets/base/mixins.scss
rename to app/assets/stylesheets/framework/mixins.scss
index c74a6d3982499eba5eeb79bb9d33add22e05ce76..089e6958eebef10cc353c4c208e4f718c27eb790 100644
--- a/app/assets/stylesheets/base/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -54,147 +54,6 @@
   @include box-shadow(0 0 0 3px #f1f1f1);
 }
 
-@mixin md-typography {
-  color: $md-text-color;
-
-  a {
-    color: $md-link-color;
-  }
-
-  img {
-    max-width: 100%;
-  }
-
-  *:first-child {
-    margin-top: 0;
-  }
-
-  code {
-    font-family: $monospace_font;
-    white-space: pre;
-    word-wrap: normal;
-    padding: 1px 2px;
-  }
-
-  kbd {
-    display: inline-block;
-    padding: 3px 5px;
-    font-size: 11px;
-    line-height: 10px;
-    color: #555;
-    vertical-align: middle;
-    background-color: #FCFCFC;
-    border-width: 1px;
-    border-style: solid;
-    border-color: #CCC #CCC #BBB;
-    border-image: none;
-    border-radius: 3px;
-    box-shadow: 0px -1px 0px #BBB inset;
-  }
-
-  h1 {
-    font-size: 1.3em;
-    font-weight: 600;
-    margin: 24px 0 12px 0;
-    padding: 0 0 10px 0;
-    border-bottom: 1px solid #e7e9ed;
-    color: #313236;
-  }
-
-  h2 {
-    font-size: 1.2em;
-    font-weight: 600;
-    margin: 24px 0 12px 0;
-    color: #313236;
-  }
-
-  h3 {
-    margin: 24px 0 12px 0;
-    font-size: 1.25em;
-  }
-
-  h4 {
-    margin: 24px 0 12px 0;
-    font-size: 1.1em;
-  }
-
-  h5 {
-    margin: 24px 0 12px 0;
-    font-size: 1em;
-  }
-
-  h6 {
-    margin: 24px 0 12px 0;
-    font-size: 0.90em;
-  }
-
-  blockquote {
-    padding: 8px 21px;
-    margin: 12px 0 12px;
-    border-left: 3px solid #e7e9ed;
-  }
-
-  blockquote p {
-    color: #7f8fa4 !important;
-    font-size: 15px;
-    line-height: 1.5;
-  }
-
-  p {
-    color:#5c5d5e;
-    margin:6px 0 0 0;
-  }
-
-  table {
-    @extend .table;
-    @extend .table-bordered;
-    margin: 12px 0 12px 0;
-    color: #5c5d5e;
-    th {
-      background: #f8fafc;
-    }
-  }
-
-  pre {
-    margin: 12px 0 12px 0 !important;
-    background-color: #f8fafc !important;
-    font-size: 13px !important;
-    color: #5b6169 !important;
-    line-height: 1.6em !important;
-    @include border-radius(2px);
-  }
-
-  p > code {
-    font-weight: inherit;
-  }
-
-
-  ul {
-    color: #5c5d5e;
-  }
-
-  li {
-    line-height: 1.6em;
-  }
-
-  a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
-    &:before {
-      margin-right: 4px;
-
-      font: normal normal normal 14px/1 FontAwesome;
-      font-size: inherit;
-      text-rendering: auto;
-      -webkit-font-smoothing: antialiased;
-      content: "\f0c6";
-    }
-
-    &:hover:before {
-      text-decoration: none;
-    }
-  }
-}
-
-
 @mixin str-truncated($max_width: 82%) {
   display: inline-block;
   overflow: hidden;
diff --git a/app/assets/stylesheets/generic/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
similarity index 91%
rename from app/assets/stylesheets/generic/mobile.scss
rename to app/assets/stylesheets/framework/mobile.scss
index 36ae126f8653e12a2b67e0f8de7c6dcd9079b4b4..cea47fba19226c8ba688f5f9882c44a7b7a29549 100644
--- a/app/assets/stylesheets/generic/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -23,7 +23,7 @@
     margin-right: 0;
   }
 
-  .issues-filters,
+  .issues-details-filters,
   .dash-projects-filters,
   .check-all-holder {
     display: none;
@@ -83,6 +83,7 @@
 
   .center-top-menu {
     height: 45px;
+    margin-bottom: 30px;
 
     li a {
       font-size: 14px;
@@ -90,9 +91,11 @@
     }
   }
 
-  .projects-search-form {
-    margin: 0 -5px !important;
+  .activity-filter-block {
+    display: none;
+  }
 
+  .projects-search-form {
     .btn {
       display: none;
     }
@@ -100,6 +103,11 @@
 }
 
 @media (max-width: $screen-sm-max) {
+  .page-with-sidebar .content-wrapper {
+    padding: 0;
+    padding-top: 1px;
+  }
+
   .issues-filters {
     .milestone-filter, .labels-filter {
       display: none;
diff --git a/app/assets/stylesheets/generic/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
similarity index 100%
rename from app/assets/stylesheets/generic/pagination.scss
rename to app/assets/stylesheets/framework/pagination.scss
diff --git a/app/assets/stylesheets/generic/selects.scss b/app/assets/stylesheets/framework/selects.scss
similarity index 100%
rename from app/assets/stylesheets/generic/selects.scss
rename to app/assets/stylesheets/framework/selects.scss
diff --git a/app/assets/stylesheets/generic/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
similarity index 100%
rename from app/assets/stylesheets/generic/sidebar.scss
rename to app/assets/stylesheets/framework/sidebar.scss
diff --git a/app/assets/stylesheets/generic/tables.scss b/app/assets/stylesheets/framework/tables.scss
similarity index 54%
rename from app/assets/stylesheets/generic/tables.scss
rename to app/assets/stylesheets/framework/tables.scss
index a66e45577de181764870e87bae2937d4b3dd6ccf..789b34020c18cc5729b9b1647e939ac0d692c474 100644
--- a/app/assets/stylesheets/generic/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -1,5 +1,21 @@
 table {
   &.table {
+    .dropdown-menu a {
+      text-decoration: none;
+    }
+
+    .success,
+    .warning,
+    .danger,
+    .info {
+      color: #fff;
+
+      a:not(.btn) {
+        text-decoration: underline;
+        color: #fff;
+      }
+    }
+
     tr {
       td, th {
         padding: 8px 10px;
@@ -12,7 +28,7 @@ table {
         border-bottom: 1px solid $border-color !important;
       }
       td {
-        border-color: #F1F1F1 !important;
+        border-color: $table-border-color !important;
         border-bottom: 1px solid;
       }
     }
diff --git a/app/assets/stylesheets/generic/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
similarity index 100%
rename from app/assets/stylesheets/generic/timeline.scss
rename to app/assets/stylesheets/framework/timeline.scss
diff --git a/app/assets/stylesheets/base/gl_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
similarity index 92%
rename from app/assets/stylesheets/base/gl_bootstrap.scss
rename to app/assets/stylesheets/framework/tw_bootstrap.scss
index eb8d23d6453cc477e8a13cbb8b4401816958920b..99d028d1228ee84bb88394051806b27943db204e 100644
--- a/app/assets/stylesheets/base/gl_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -32,8 +32,6 @@
 @import "bootstrap/pager";
 @import "bootstrap/labels";
 @import "bootstrap/badges";
-@import "bootstrap/jumbotron";
-@import "bootstrap/thumbnails";
 @import "bootstrap/alerts";
 @import "bootstrap/progress-bars";
 @import "bootstrap/list-group";
@@ -251,23 +249,3 @@
 .text-info:hover {
   color: $brand-info;
 }
-
-// Tables =====================================================================
-
-table.table {
-  .dropdown-menu a {
-    text-decoration: none;
-  }
-
-  .success,
-  .warning,
-  .danger,
-  .info {
-    color: #fff;
-
-    a:not(.btn) {
-      text-decoration: underline;
-      color: #fff;
-    }
-  }
-}
diff --git a/app/assets/stylesheets/base/gl_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
similarity index 99%
rename from app/assets/stylesheets/base/gl_variables.scss
rename to app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 18632da4f2a660dfeb6e1177f28dd98ac4402f23..63868a34e2a1aa4855e561f25540ae746683f8af 100644
--- a/app/assets/stylesheets/base/gl_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -156,3 +156,5 @@ $nav-link-padding: 13px $gl-padding;
 $pre-bg:           #f8fafc !default;
 $pre-color:        $gl-gray !default;
 $pre-border-color: #e7e9ed;
+
+$table-bg-accent: $background-color;
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
new file mode 100644
index 0000000000000000000000000000000000000000..bf36f96cc97f225c8735e202cf2b3248ef5828c2
--- /dev/null
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -0,0 +1,271 @@
+@mixin md-typography {
+  color: $md-text-color;
+
+  a {
+    color: $md-link-color;
+  }
+
+  img {
+    max-width: 100%;
+  }
+
+  *:first-child {
+    margin-top: 0;
+  }
+
+  code {
+    font-family: $monospace_font;
+    white-space: pre;
+    word-wrap: normal;
+    padding: 1px 2px;
+  }
+
+  kbd {
+    display: inline-block;
+    padding: 3px 5px;
+    font-size: 11px;
+    line-height: 10px;
+    color: #555;
+    vertical-align: middle;
+    background-color: #FCFCFC;
+    border-width: 1px;
+    border-style: solid;
+    border-color: #CCC #CCC #BBB;
+    border-image: none;
+    border-radius: 3px;
+    box-shadow: 0px -1px 0px #BBB inset;
+  }
+
+  h1 {
+    font-size: 1.3em;
+    font-weight: 600;
+    margin: 24px 0 12px 0;
+    padding: 0 0 10px 0;
+    border-bottom: 1px solid #e7e9ed;
+    color: #313236;
+  }
+
+  h2 {
+    font-size: 1.2em;
+    font-weight: 600;
+    margin: 24px 0 12px 0;
+    color: #313236;
+  }
+
+  h3 {
+    margin: 24px 0 12px 0;
+    font-size: 1.25em;
+  }
+
+  h4 {
+    margin: 24px 0 12px 0;
+    font-size: 1.1em;
+  }
+
+  h5 {
+    margin: 24px 0 12px 0;
+    font-size: 1em;
+  }
+
+  h6 {
+    margin: 24px 0 12px 0;
+    font-size: 0.90em;
+  }
+
+  blockquote {
+    padding: 8px 21px;
+    margin: 12px 0 12px;
+    border-left: 3px solid #e7e9ed;
+  }
+
+  blockquote p {
+    color: #7f8fa4 !important;
+    font-size: 15px;
+    line-height: 1.5;
+  }
+
+  p {
+    color:#5c5d5e;
+    margin:6px 0 0 0;
+  }
+
+  table {
+    @extend .table;
+    @extend .table-bordered;
+    margin: 12px 0 12px 0;
+    color: #5c5d5e;
+    th {
+      background: #f8fafc;
+    }
+  }
+
+  pre {
+    margin: 12px 0 12px 0 !important;
+    background-color: #f8fafc !important;
+    font-size: 13px !important;
+    color: #5b6169 !important;
+    line-height: 1.6em !important;
+    @include border-radius(2px);
+  }
+
+  p > code {
+    font-weight: inherit;
+  }
+
+
+  ul {
+    color: #5c5d5e;
+  }
+
+  li {
+    line-height: 1.6em;
+  }
+
+  a[href*="/uploads/"], a[href*="storage.googleapis.com/google-code-attachments/"] {
+    &:before {
+      margin-right: 4px;
+
+      font: normal normal normal 14px/1 FontAwesome;
+      font-size: inherit;
+      text-rendering: auto;
+      -webkit-font-smoothing: antialiased;
+      content: "\f0c6";
+    }
+
+    &:hover:before {
+      text-decoration: none;
+    }
+  }
+}
+
+
+/**
+ * Headers
+ *
+ */
+body {
+  text-rendering:optimizeLegibility;
+  -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
+}
+
+.page-title {
+  margin-top: 0px;
+  line-height: 1.3;
+  font-size: 1.25em;
+  font-weight: 600;
+}
+
+.page-title-empty {
+  margin-top: 0px;
+  line-height: 1.3;
+  font-size: 1.25em;
+  font-weight: 600;
+  margin: 12px 7px 12px 7px;
+}
+
+h1, h2, h3, h4, h5, h6 {
+  color: $gl-header-color;
+  font-weight: 500;
+}
+
+/** CODE **/
+pre {
+  font-family: $monospace_font;
+
+  &.dark {
+    background: #333;
+    color: $background-color;
+  }
+
+  &.plain-readme {
+    background: none;
+    border: none;
+    padding: 0;
+    margin: 0;
+    font-size: 14px;
+  }
+}
+
+.monospace {
+  font-family: $monospace_font;
+}
+
+code {
+  &.key-fingerprint {
+    background: $body-bg;
+    color: $text-color;
+  }
+}
+
+a > code {
+  color: $link-color;
+}
+
+/**
+ * Wiki typography
+ *
+ */
+.wiki {
+  @include md-typography;
+
+  word-wrap: break-word;
+  padding: 7px;
+
+  /* Link to current header. */
+  h1, h2, h3, h4, h5, h6 {
+    position: relative;
+
+    a.anchor {
+      // Setting `display: none` would prevent the anchor being scrolled to, so
+      // instead we set the height to 0 and it gets updated on hover.
+      height: 0;
+    }
+
+    &:hover > a.anchor {
+      $size: 16px;
+      position: absolute;
+      right: 100%;
+      top: 50%;
+      margin-top: -$size/2;
+      margin-right: 0px;
+      padding-right: 20px;
+      display: inline-block;
+      width: $size;
+      height: $size;
+      background-image: image-url("icon-link.png");
+      background-size: contain;
+      background-repeat: no-repeat;
+    }
+  }
+
+  ul,ol {
+    padding: 0;
+    margin: 6px 0 6px 18px !important;
+  }
+  ol {
+    color: #5c5d5e;
+  }
+}
+
+.md-area {
+  @include md-typography;
+}
+
+.md {
+  @include md-typography;
+}
+
+/**
+ * Textareas intended for GFM
+ *
+ */
+textarea.js-gfm-input {
+  font-family: $monospace_font;
+}
+
+.md-preview {
+}
+
+.strikethrough {
+  text-decoration: line-through;
+}
diff --git a/app/assets/stylesheets/base/variables.scss b/app/assets/stylesheets/framework/variables.scss
similarity index 98%
rename from app/assets/stylesheets/base/variables.scss
rename to app/assets/stylesheets/framework/variables.scss
index eb9a2966389f4ea389bc27fe9a7a1fe9d58cc904..91954683c3e8db28989abfd7fd970c2e35cd3a73 100644
--- a/app/assets/stylesheets/base/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -16,6 +16,7 @@ $avatar_radius: 50%;
 $code_font_size: 13px;
 $code_line_height: 1.5;
 $border-color: #dce0e6;
+$table-border-color: #eef0f2;
 $background-color: #F7F8FA;
 $header-height: 58px;
 $fixed-layout-width: 1200px;
diff --git a/app/assets/stylesheets/generic/zen.scss b/app/assets/stylesheets/framework/zen.scss
similarity index 100%
rename from app/assets/stylesheets/generic/zen.scss
rename to app/assets/stylesheets/framework/zen.scss
diff --git a/app/assets/stylesheets/generic/typography.scss b/app/assets/stylesheets/generic/typography.scss
deleted file mode 100644
index 6a3cb49baaefc45125a369054aac6a8245d951b0..0000000000000000000000000000000000000000
--- a/app/assets/stylesheets/generic/typography.scss
+++ /dev/null
@@ -1,130 +0,0 @@
-/**
- * Headers
- *
- */
-body {
-  text-rendering:optimizeLegibility;
-  -webkit-text-shadow: rgba(255,255,255,0.01) 0 0 1px;
-}
- 
-.page-title {
-  margin-top: 0px;
-  line-height: 1.3;
-  font-size: 1.25em;
-  font-weight: 600;
-}
-
-.page-title-empty {
-  margin-top: 0px;
-  line-height: 1.3;
-  font-size: 1.25em;
-  font-weight: 600;
-  margin: 12px 7px 12px 7px;
-}
-
-h1, h2, h3, h4, h5, h6 {
-  color: $gl-header-color;
-  font-weight: 500;
-}
-
-/** CODE **/
-pre {
-  font-family: $monospace_font;
-
-  &.dark {
-    background: #333;
-    color: $background-color;
-  }
-
-  &.plain-readme {
-    background: none;
-    border: none;
-    padding: 0;
-    margin: 0;
-    font-size: 14px;
-  }
-}
-
-.monospace {
-  font-family: $monospace_font;
-}
-
-code {
-  &.key-fingerprint {
-    background: $body-bg;
-    color: $text-color;
-  }
-}
-
-a > code {
-  color: $link-color;
-}
-
-/**
- * Wiki typography
- *
- */
-.wiki {
-  @include md-typography;
-
-  word-wrap: break-word;
-  padding: 7px;
-
-  /* Link to current header. */
-  h1, h2, h3, h4, h5, h6 {
-    position: relative;
-
-    a.anchor {
-      // Setting `display: none` would prevent the anchor being scrolled to, so
-      // instead we set the height to 0 and it gets updated on hover.
-      height: 0;
-    }
-
-    &:hover > a.anchor {
-      $size: 16px;
-      position: absolute;
-      right: 100%;
-      top: 50%;
-      margin-top: -$size/2;
-      margin-right: 0px;
-      padding-right: 20px;
-      display: inline-block;
-      width: $size;
-      height: $size;
-      background-image: image-url("icon-link.png");
-      background-size: contain;
-      background-repeat: no-repeat;
-    }
-  }
-
-  ul,ol {
-    padding: 0;
-    margin: 6px 0 6px 18px !important;
-  }
-  ol {
-    color: #5c5d5e;
-  }
-}
-
-.md-area {
-  @include md-typography;
-}
-
-.md {
-  @include md-typography;
-}
-
-/**
- * Textareas intended for GFM
- *
- */
-textarea.js-gfm-input {
-  font-family: $monospace_font;
-}
-
-.md-preview {
-}
-
-.strikethrough {
-  text-decoration: line-through;
-}
\ No newline at end of file
diff --git a/app/assets/stylesheets/ci/builds.scss b/app/assets/stylesheets/pages/builds.scss
similarity index 100%
rename from app/assets/stylesheets/ci/builds.scss
rename to app/assets/stylesheets/pages/builds.scss
diff --git a/app/assets/stylesheets/ci/projects.scss b/app/assets/stylesheets/pages/ci_projects.scss
similarity index 100%
rename from app/assets/stylesheets/ci/projects.scss
rename to app/assets/stylesheets/pages/ci_projects.scss
diff --git a/app/assets/stylesheets/ci/lint.scss b/app/assets/stylesheets/pages/lint.scss
similarity index 100%
rename from app/assets/stylesheets/ci/lint.scss
rename to app/assets/stylesheets/pages/lint.scss
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index dcd1aed71962b399c2af49ef779ffb05638b4a05..4392f08942b6d939daecfe9ab670f8a955496adf 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -65,7 +65,6 @@
 
 .note-image-attach {
   @extend .col-md-4;
-  @extend .thumbnail;
   margin-left: 45px;
   float: none;
 }
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index 0031ab5151b6f1859fd6f9c597138bd17df023ff..f7a2284900398d3767ddf1814621f7e76f8a3fa5 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -63,6 +63,7 @@
     }
 
     p {
+      padding: 0 $gl-padding;
       color: #5c5d5e;
     }
   }
@@ -510,8 +511,3 @@ pre.light-well {
     margin-top: -1px;
   }
 }
-
-.inline-form {
-  display: inline-block;
-}
-
diff --git a/app/assets/stylesheets/ci/runners.scss b/app/assets/stylesheets/pages/runners.scss
similarity index 100%
rename from app/assets/stylesheets/ci/runners.scss
rename to app/assets/stylesheets/pages/runners.scss
diff --git a/app/assets/stylesheets/ci/status.scss b/app/assets/stylesheets/pages/status.scss
similarity index 100%
rename from app/assets/stylesheets/ci/status.scss
rename to app/assets/stylesheets/pages/status.scss
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 271cc547e2b31faa7c101b4b5c9242d29d9bc529..dadd86e88ccde1e062b438467d573f6c70044f95 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,7 +1,7 @@
 .tree-holder {
-  .tree-content-holder {
-    float: left;
-    width: 100%;
+  .tree-table-holder {
+    margin-left: -$gl-padding;
+    margin-right: -$gl-padding;
   }
 
   .tree_progress {
@@ -13,10 +13,15 @@
   }
 
   .tree-table {
-    @extend .table;
-    @include border-radius(0);
+    margin-bottom: 0;
 
     tr {
+      > td, > th {
+        padding: 10px $gl-padding;
+        line-height: 32px;
+        border-color: $table-border-color !important;
+      }
+
       &:hover {
         td {
           background: $hover;
@@ -27,9 +32,9 @@
       }
       &.selected {
         td {
-          background: $background-color;
-          border-top: 1px solid #EEE;
-          border-bottom: 1px solid #EEE;
+          background: $gray-dark;
+          border-top: 1px solid $border-gray-dark;
+          border-bottom: 1px solid $border-gray-dark;
         }
       }
     }
@@ -85,19 +90,6 @@
   margin-right: 15px;
 }
 
-.readme-holder {
-  margin: 0 auto;
-
-  .readme-file-title {
-    font-size: 14px;
-    font-weight: bold;
-    margin-bottom: 20px;
-    color: #777;
-    border-bottom: 1px solid #DDD;
-    padding: 10px 0;
-  }
-}
-
 .blob-commit-info {
   list-style: none;
   margin: 0;
diff --git a/app/assets/stylesheets/ci/xterm.scss b/app/assets/stylesheets/pages/xterm.scss
similarity index 100%
rename from app/assets/stylesheets/ci/xterm.scss
rename to app/assets/stylesheets/pages/xterm.scss
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index 524218290c6635323bd2e038803cd2501560aafa..40fb15a5b3682b50fc00e96c9e3766f54869889f 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -88,7 +88,7 @@ class GroupsController < Groups::ApplicationController
   def destroy
     DestroyGroupService.new(@group, current_user).execute
 
-    redirect_to root_path, alert: "Group '#{@group.name} was deleted."
+    redirect_to root_path, alert: "Group '#{@group.name}' was successfully deleted."
   end
 
   protected
diff --git a/app/models/ability.rb b/app/models/ability.rb
index a020b24a550f6fe75f2830b37228666fdafcc1c9..77c121ca5e851bd40b7f10956ad0dda5fa06b80d 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -135,6 +135,8 @@ class Ability
 
     def project_report_rules
       project_guest_rules + [
+        :create_commit_status,
+        :read_commit_statuses,
         :download_code,
         :fork_project,
         :create_project_snippet,
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5d17f4418ed1dc584540ea652206d2dde932b01c..f8c731a7bf7a1c4af00561ef3f2afef031626ed1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -24,32 +24,19 @@
 #
 
 module Ci
-  class Build < ActiveRecord::Base
-    extend Ci::Model
-
+  class Build < CommitStatus
     LAZY_ATTRIBUTES = ['trace']
 
-    belongs_to :commit, class_name: 'Ci::Commit'
     belongs_to :runner, class_name: 'Ci::Runner'
     belongs_to :trigger_request, class_name: 'Ci::TriggerRequest'
-    belongs_to :user
 
     serialize :options
 
-    validates :commit, presence: true
-    validates :status, presence: true
     validates :coverage, numericality: true, allow_blank: true
     validates_presence_of :ref
 
-    scope :running, ->() { where(status: "running") }
-    scope :pending, ->() { where(status: "pending") }
-    scope :success, ->() { where(status: "success") }
-    scope :failed, ->() { where(status: "failed")  }
     scope :unstarted, ->() { where(runner_id: nil) }
-    scope :running_or_pending, ->() { where(status:[:running, :pending]) }
-    scope :latest, ->() { where(id: unscope(:select).select('max(id)').group(:name, :ref)).order(stage_idx: :asc) }
     scope :ignore_failures, ->() { where(allow_failure: false) }
-    scope :for_ref, ->(ref) { where(ref: ref) }
     scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
 
     acts_as_taggable
@@ -74,13 +61,14 @@ module Ci
 
       def create_from(build)
         new_build = build.dup
-        new_build.status = :pending
+        new_build.status = 'pending'
         new_build.runner_id = nil
+        new_build.trigger_request_id = nil
         new_build.save
       end
 
       def retry(build)
-        new_build = Ci::Build.new(status: :pending)
+        new_build = Ci::Build.new(status: 'pending')
         new_build.ref = build.ref
         new_build.tag = build.tag
         new_build.options = build.options
@@ -98,28 +86,7 @@ module Ci
     end
 
     state_machine :status, initial: :pending do
-      event :run do
-        transition pending: :running
-      end
-
-      event :drop do
-        transition running: :failed
-      end
-
-      event :success do
-        transition running: :success
-      end
-
-      event :cancel do
-        transition [:pending, :running] => :canceled
-      end
-
-      after_transition pending: :running do |build, transition|
-        build.update_attributes started_at: Time.now
-      end
-
       after_transition any => [:success, :failed, :canceled] do |build, transition|
-        build.update_attributes finished_at: Time.now
         project = build.project
 
         if project.web_hooks?
@@ -136,19 +103,10 @@ module Ci
           build.update_coverage
         end
       end
-
-      state :pending, value: 'pending'
-      state :running, value: 'running'
-      state :failed, value: 'failed'
-      state :success, value: 'success'
-      state :canceled, value: 'canceled'
     end
 
-    delegate :sha, :short_sha, :project, :gl_project,
-      to: :commit, prefix: false
-
-    def before_sha
-      Gitlab::Git::BLANK_SHA
+    def ignored?
+      failed? && allow_failure?
     end
 
     def trace_html
@@ -156,22 +114,6 @@ module Ci
       html || ''
     end
 
-    def started?
-      !pending? && !canceled? && started_at
-    end
-
-    def active?
-      running? || pending?
-    end
-
-    def complete?
-      canceled? || success? || failed?
-    end
-
-    def ignored?
-      failed? && allow_failure?
-    end
-
     def timeout
       project.timeout
     end
@@ -180,14 +122,6 @@ module Ci
       yaml_variables + project_variables + trigger_variables
     end
 
-    def duration
-      if started_at && finished_at
-        finished_at - started_at
-      elsif started_at
-        Time.now - started_at
-      end
-    end
-
     def project
       commit.project
     end
@@ -278,6 +212,25 @@ module Ci
       "#{dir_to_trace}/#{id}.log"
     end
 
+    def target_url
+      Gitlab::Application.routes.url_helpers.
+        namespace_project_build_url(gl_project.namespace, gl_project, self)
+    end
+
+    def cancel_url
+      if active?
+        Gitlab::Application.routes.url_helpers.
+          cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url)
+      end
+    end
+
+    def retry_url
+      if commands.present?
+        Gitlab::Application.routes.url_helpers.
+          cancel_namespace_project_build_path(gl_project.namespace, gl_project, self, return_to: request.original_url)
+      end
+    end
+
     private
 
     def yaml_variables
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index fde754a92a16ab513eb0f37f090cf98f64aed9c8..68864edfbbf40155b140a4ac64b22ca7dd29278e 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -20,7 +20,8 @@ module Ci
     extend Ci::Model
 
     belongs_to :gl_project, class_name: '::Project', foreign_key: :gl_project_id
-    has_many :builds, dependent: :destroy, class_name: 'Ci::Build'
+    has_many :statuses, dependent: :destroy, class_name: 'CommitStatus'
+    has_many :builds, class_name: 'Ci::Build'
     has_many :trigger_requests, dependent: :destroy, class_name: 'Ci::TriggerRequest'
 
     validates_presence_of :sha
@@ -47,7 +48,7 @@ module Ci
     end
 
     def retry
-      builds_without_retry.each do |build|
+      latest_builds.each do |build|
         Ci::Build.retry(build)
       end
     end
@@ -81,12 +82,11 @@ module Ci
     end
 
     def stage
-      running_or_pending = builds_without_retry.running_or_pending
-      running_or_pending.limit(1).pluck(:stage).first
+      running_or_pending = statuses.latest.running_or_pending.ordered
+      running_or_pending.first.try(:stage)
     end
 
     def create_builds(ref, tag, user, trigger_request = nil)
-      return if skip_ci? && trigger_request.blank?
       return unless config_processor
       config_processor.stages.any? do |stage|
         CreateBuildsService.new.execute(self, stage, ref, tag, user, trigger_request).present?
@@ -94,7 +94,6 @@ module Ci
     end
 
     def create_next_builds(ref, tag, user, trigger_request)
-      return if skip_ci? && trigger_request.blank?
       return unless config_processor
 
       stages = builds.where(ref: ref, tag: tag, trigger_request: trigger_request).group_by(&:stage)
@@ -107,61 +106,60 @@ module Ci
     end
 
     def refs
-      builds.group(:ref).pluck(:ref)
+      statuses.order(:ref).pluck(:ref).uniq
     end
 
-    def last_ref
-      builds.latest.first.try(:ref)
+    def latest_statuses
+      @latest_statuses ||= statuses.latest.to_a
     end
 
-    def builds_without_retry
-      builds.latest
+    def latest_builds
+      @latest_builds ||= builds.latest.to_a
     end
 
-    def builds_without_retry_for_ref(ref)
-      builds.for_ref(ref).latest
+    def latest_builds_for_ref(ref)
+      latest_builds.select { |build| build.ref == ref }
     end
 
-    def retried_builds
-      @retried_builds ||= (builds.order(id: :desc) - builds_without_retry)
+    def retried
+      @retried ||= (statuses.order(id: :desc) - statuses.latest)
     end
 
     def status
-      if skip_ci?
-        return 'skipped'
-      elsif yaml_errors.present?
+      if yaml_errors.present?
         return 'failed'
-      elsif builds.none?
-        return 'skipped'
-      elsif success?
-        'success'
-      elsif pending?
-        'pending'
-      elsif running?
-        'running'
-      elsif canceled?
-        'canceled'
-      else
-        'failed'
+      end
+
+      @status ||= begin
+        latest = latest_statuses
+        latest.reject! { |status| status.try(&:allow_failure?) }
+
+        if latest.none?
+          'skipped'
+        elsif latest.all?(&:success?)
+          'success'
+        elsif latest.all?(&:pending?)
+          'pending'
+        elsif latest.any?(&:running?) || latest.any?(&:pending?)
+          'running'
+        elsif latest.all?(&:canceled?)
+          'canceled'
+        else
+          'failed'
+        end
       end
     end
 
     def pending?
-      builds_without_retry.all? do |build|
-        build.pending?
-      end
+      status == 'pending'
     end
 
     def running?
-      builds_without_retry.any? do |build|
-        build.running? || build.pending?
-      end
+      status == 'running'
     end
 
     def success?
-      builds_without_retry.all? do |build|
-        build.success? || build.ignored?
-      end
+      status == 'success'
     end
 
     def failed?
@@ -169,26 +167,21 @@ module Ci
     end
 
     def canceled?
-      builds_without_retry.all? do |build|
-        build.canceled?
-      end
+      status == 'canceled'
     end
 
     def duration
-      @duration ||= builds_without_retry.select(&:duration).sum(&:duration).to_i
-    end
-
-    def duration_for_ref(ref)
-      builds_without_retry_for_ref(ref).select(&:duration).sum(&:duration).to_i
+      duration_array = latest_statuses.map(&:duration).compact
+      duration_array.reduce(:+).to_i
     end
 
     def finished_at
-      @finished_at ||= builds.order('finished_at DESC').first.try(:finished_at)
+      @finished_at ||= statuses.order('finished_at DESC').first.try(:finished_at)
     end
 
     def coverage
       if project.coverage_enabled?
-        coverage_array = builds_without_retry.map(&:coverage).compact
+        coverage_array = latest_builds.map(&:coverage).compact
         if coverage_array.size >= 1
           '%.2f' % (coverage_array.reduce(:+) / coverage_array.size)
         end
@@ -196,7 +189,7 @@ module Ci
     end
 
     def matrix_for_ref?(ref)
-      builds_without_retry_for_ref(ref).pluck(:id).size > 1
+      latest_builds_for_ref(ref).size > 1
     end
 
     def config_processor
@@ -217,7 +210,6 @@ module Ci
     end
 
     def skip_ci?
-      return false if builds.any?
       git_commit_message =~ /(\[ci skip\])/ if git_commit_message
     end
 
diff --git a/app/models/commit.rb b/app/models/commit.rb
index aff329d71fa3f29e657597531ea161bfd3c90b68..d5c50013525a8f17575bf6ae80557df1a0bf5f17 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -184,4 +184,12 @@ class Commit
   def parents
     @parents ||= Commit.decorate(super, project)
   end
+
+  def ci_commit
+    project.ci_commit(sha)
+  end
+
+  def status
+    ci_commit.try(:status) || :not_found
+  end
 end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b4d91b1b0c321b7c16793d32d20c7f4c24a2a3c1
--- /dev/null
+++ b/app/models/commit_status.rb
@@ -0,0 +1,91 @@
+class CommitStatus < ActiveRecord::Base
+  self.table_name = 'ci_builds'
+
+  belongs_to :commit, class_name: 'Ci::Commit'
+  belongs_to :user
+
+  validates :commit, presence: true
+  validates :status, inclusion: { in: %w(pending running failed success canceled) }
+
+  validates_presence_of :name
+
+  alias_attribute :author, :user
+
+  scope :running, -> { where(status: 'running') }
+  scope :pending, -> { where(status: 'pending') }
+  scope :success, -> { where(status: 'success') }
+  scope :failed, -> { where(status: 'failed')  }
+  scope :running_or_pending, -> { where(status:[:running, :pending]) }
+  scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :ref)) }
+  scope :ordered, -> { order(:ref, :stage_idx, :name) }
+  scope :for_ref, ->(ref) { where(ref: ref) }
+  scope :running_or_pending, -> { where(status: [:running, :pending]) }
+
+  state_machine :status, initial: :pending do
+    event :run do
+      transition pending: :running
+    end
+
+    event :drop do
+      transition running: :failed
+    end
+
+    event :success do
+      transition [:pending, :running] => :success
+    end
+
+    event :cancel do
+      transition [:pending, :running] => :canceled
+    end
+
+    after_transition pending: :running do |build, transition|
+      build.update_attributes started_at: Time.now
+    end
+
+    after_transition any => [:success, :failed, :canceled] do |build, transition|
+      build.update_attributes finished_at: Time.now
+    end
+
+    state :pending, value: 'pending'
+    state :running, value: 'running'
+    state :failed, value: 'failed'
+    state :success, value: 'success'
+    state :canceled, value: 'canceled'
+  end
+
+  delegate :sha, :short_sha, :gl_project,
+           to: :commit, prefix: false
+
+  # TODO: this should be removed with all references
+  def before_sha
+    Gitlab::Git::BLANK_SHA
+  end
+
+  def started?
+    !pending? && !canceled? && started_at
+  end
+
+  def active?
+    running? || pending?
+  end
+
+  def complete?
+    canceled? || success? || failed?
+  end
+
+  def duration
+    if started_at && finished_at
+      finished_at - started_at
+    elsif started_at
+      Time.now - started_at
+    end
+  end
+
+  def cancel_url
+    nil
+  end
+
+  def retry_url
+    nil
+  end
+end
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fa54e3540d07bd99637c76b65d7b53ba4acc58c5
--- /dev/null
+++ b/app/models/generic_commit_status.rb
@@ -0,0 +1,15 @@
+class GenericCommitStatus < CommitStatus
+  before_validation :set_default_values
+
+  # GitHub compatible API
+  alias_attribute :context, :name
+
+  def set_default_values
+    self.context ||= 'default'
+    self.stage ||= 'external'
+  end
+
+  def tags
+    [:external]
+  end
+end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index eb468c6cd53b162140dbd58c987c9cd1740f72be..c83b15c7d392d355cf166a7358d21f6aac88d5d1 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -227,7 +227,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def work_in_progress?
-    title =~ /\A\[?WIP\]?:? /i
+    !!(title =~ /\A\[?WIP\]?:? /i)
   end
 
   def mergeable?
@@ -275,7 +275,8 @@ class MergeRequest < ActiveRecord::Base
     attrs = {
       source: source_project.hook_attrs,
       target: target_project.hook_attrs,
-      last_commit: nil
+      last_commit: nil,
+      work_in_progress: work_in_progress?
     }
 
     unless last_commit.nil?
diff --git a/app/models/project_services/bamboo_service.rb b/app/models/project_services/bamboo_service.rb
index d8aedbd2ab48c86726ccd7c0e472af2133859971..5f5255ab48727398a285a8d2247b222189de587a 100644
--- a/app/models/project_services/bamboo_service.rb
+++ b/app/models/project_services/bamboo_service.rb
@@ -40,12 +40,19 @@ class BambooService < CiService
   attr_accessor :response
 
   after_save :compose_service_hook, if: :activated?
+  before_update :reset_password
 
   def compose_service_hook
     hook = service_hook || build_service_hook
     hook.save
   end
 
+  def reset_password
+    if prop_updated?(:bamboo_url)
+      self.password = nil
+    end
+  end
+
   def title
     'Atlassian Bamboo CI'
   end
diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
index 0e6e97394bc67c954e2048a1f206cf9fdcf5905e..f17993d9f3bc7df6451f1fd2c3d801cfad4b231e 100644
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ b/app/models/project_services/ci/hip_chat_service.rb
@@ -49,7 +49,7 @@ module Ci
 
       commit = build.commit
       return unless commit
-      return unless commit.builds_without_retry.include? build
+      return unless commit.latest_builds.include? build
 
       case commit.status.to_sym
       when :failed
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index 11a2743f9693a6b41ed3aed7bb33e19e2c8201d0..fd1933010015022130be19cefe1a3e36500ea9bf 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -48,7 +48,7 @@ module Ci
       # it doesn't make sense to send emails for retried builds
       commit = build.commit
       return unless commit
-      return unless commit.builds_without_retry.include?(build)
+      return unless commit.latest_builds.include?(build)
 
       case build.status.to_sym
       when :failed
diff --git a/app/models/project_services/ci/slack_message.rb b/app/models/project_services/ci/slack_message.rb
index 5ac8907ecd0de72a58344d62af10e44c9fd5fc96..dc050a3fc59973a8890e5f6e09daa833af3931e4 100644
--- a/app/models/project_services/ci/slack_message.rb
+++ b/app/models/project_services/ci/slack_message.rb
@@ -23,7 +23,7 @@ module Ci
     def attachments
       fields = []
 
-      commit.builds_without_retry.each do |build|
+      commit.latest_builds.each do |build|
         next if build.allow_failure?
         next unless build.failed?
         fields << {
diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
index 76db573dc17a52b7e5b0ab63dd58e2b09ba3ee62..ee8e49888263c31d1daa95a07c785feaf587576e 100644
--- a/app/models/project_services/ci/slack_service.rb
+++ b/app/models/project_services/ci/slack_service.rb
@@ -48,7 +48,7 @@ module Ci
 
       commit = build.commit
       return unless commit
-      return unless commit.builds_without_retry.include?(build)
+      return unless commit.latest_builds.include?(build)
 
       case commit.status.to_sym
       when :failed
diff --git a/app/models/project_services/teamcity_service.rb b/app/models/project_services/teamcity_service.rb
index 3c002a1634b8c9535e577f829145e27f1c224c9b..fb11cad352ed014e3ac818a6bb2cbbf3c24d04ac 100644
--- a/app/models/project_services/teamcity_service.rb
+++ b/app/models/project_services/teamcity_service.rb
@@ -37,12 +37,19 @@ class TeamcityService < CiService
   attr_accessor :response
 
   after_save :compose_service_hook, if: :activated?
+  before_update :reset_password
 
   def compose_service_hook
     hook = service_hook || build_service_hook
     hook.save
   end
 
+  def reset_password
+    if prop_updated?(:teamcity_url)
+      self.password = nil
+    end
+  end
+
   def title
     'JetBrains TeamCity CI'
   end
diff --git a/app/models/service.rb b/app/models/service.rb
index 60fcc9d2857a50dfdff4a9cddc51ab62b1d3b3fc..7e845d565b1c4c7cdcc712d89621b49d2f37885a 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -117,6 +117,15 @@ class Service < ActiveRecord::Base
     end
   end
 
+  # ActiveRecord does not provide a mechanism to track changes in serialized keys.
+  # This is why we need to perform extra query to do it mannually.
+  def prop_updated?(prop_name)
+    relation_name = self.type.underscore
+    previous_value = project.send(relation_name).send(prop_name)
+    return false if previous_value.nil?
+    previous_value != send(prop_name)
+  end
+
   def async_execute(data)
     return unless supported_events.include?(data[:object_kind])
 
diff --git a/app/services/ci/create_commit_service.rb b/app/services/ci/create_commit_service.rb
index fc1ae5774d561b6c96b8cd3f4f3bceaec2bfd997..479a2d6defc5e28b722512d02d9621178f83c8bd 100644
--- a/app/services/ci/create_commit_service.rb
+++ b/app/services/ci/create_commit_service.rb
@@ -17,8 +17,10 @@ module Ci
 
       tag = origin_ref.start_with?('refs/tags/')
       commit = project.gl_project.ensure_ci_commit(sha)
-      commit.update_committed!
-      commit.create_builds(ref, tag, user)
+      unless commit.skip_ci?
+        commit.update_committed!
+        commit.create_builds(ref, tag, user)
+      end
 
       commit
     end
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index 97e4bb2fd73e670e470fa5f701717c99afc00506..bc08458312c4b74b5ef4c0804d3f97f5ee624be7 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -32,7 +32,7 @@
       %hr
       = form_tag admin_users_path, method: :get, class: 'form-inline' do
         .form-group
-          = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control'
+          = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false
           = hidden_field_tag "filter", params[:filter]
         = button_tag class: 'btn btn-primary' do
           %i.fa.fa-search
diff --git a/app/views/ci/admin/runners/index.html.haml b/app/views/ci/admin/runners/index.html.haml
index b9d6703ff4104d9e5381b87139c1dee98a2781f4..01ce81b44764d8dd4e45b3aea5209d25cd0e50b4 100644
--- a/app/views/ci/admin/runners/index.html.haml
+++ b/app/views/ci/admin/runners/index.html.haml
@@ -27,7 +27,7 @@
   .pull-left
     = form_tag ci_admin_runners_path, id: 'runners-search', class: 'form-inline', method: :get do
       .form-group
-        = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token'
+        = search_field_tag :search, params[:search], class: 'form-control', placeholder: 'Runner description or token', spellcheck: false
       = submit_tag 'Search', class: 'btn'
 
   .pull-right.light
diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml
index 5bb442cbf9227a3ca59f1520273b42574d536cf8..92787b2e6ac933b0d7dae3f9e2388dde4e068f41 100644
--- a/app/views/ci/admin/runners/show.html.haml
+++ b/app/views/ci/admin/runners/show.html.haml
@@ -76,7 +76,7 @@
         %td
           = form_tag ci_admin_runner_path(@runner), id: 'runner-projects-search', class: 'form-inline', method: :get do
             .form-group
-              = search_field_tag :search, params[:search], class: 'form-control'
+              = search_field_tag :search, params[:search], class: 'form-control', spellcheck: false
             = submit_tag 'Search', class: 'btn'
 
         %td
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index 19d919f9b6a99a456d12452899d0a3ada744d448..f98fd9f06ba707c49ae8325da944e1a54198c500 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -3,10 +3,9 @@
 
 .gray-content-block
   - if current_user
-    %ul.nav.nav-pills.event_filter.pull-right
-      %li.pull-right
-        = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'rss-btn' do
-          %i.fa.fa-rss
+    .pull-right
+      = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do
+        %i.fa.fa-rss
   = render 'shared/event_filter'
 
 .content_list
diff --git a/app/views/dashboard/projects/_projects.html.haml b/app/views/dashboard/projects/_projects.html.haml
index e09e032a7f1c92111c2614b06051428c10059a24..d0194a17b01e6ee83da4796ff0209745004098f9 100644
--- a/app/views/dashboard/projects/_projects.html.haml
+++ b/app/views/dashboard/projects/_projects.html.haml
@@ -1,7 +1,7 @@
 .projects-list-holder
   .projects-search-form
     .input-group
-      = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
+      = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
       - if current_user.can_create_project?
         %span.input-group-btn
           = link_to new_project_path, class: 'btn btn-green' do
diff --git a/app/views/explore/groups/index.html.haml b/app/views/explore/groups/index.html.haml
index 83d4d321c8311bdd0fac3ba20524f27cb7de5da9..fcb07b04083ae03470f5f608e267b66a74426484 100644
--- a/app/views/explore/groups/index.html.haml
+++ b/app/views/explore/groups/index.html.haml
@@ -11,7 +11,7 @@
     = form_tag explore_groups_path, method: :get, class: 'form-inline form-tiny' do |f|
       = hidden_field_tag :sort, @sort
       .form-group
-        = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search"
+        = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "groups_search", spellcheck: false
       .form-group
         = button_tag 'Search', class: "btn btn-default"
 
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 5a3d689d1e5ce42887a7639b38d2ef4b4d411605..2761272aa8ab15d40a2d575da4f2da92bbf391e6 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -1,7 +1,7 @@
 .pull-left
   = form_tag explore_projects_filter_path, method: :get, class: 'form-inline form-tiny' do |f|
     .form-group
-      = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search"
+      = search_field_tag :search, params[:search], placeholder: "Filter by name", class: "form-control search-text-input", id: "projects_search", spellcheck: false
     .form-group
       = button_tag 'Search', class: "btn btn-success"
 
diff --git a/app/views/groups/_projects.html.haml b/app/views/groups/_projects.html.haml
index 2b27a88794d31966b8277f370ea6b203d1dbe1bc..133f3e2d5a8bad3e82e9dbe870571e61031ed03f 100644
--- a/app/views/groups/_projects.html.haml
+++ b/app/views/groups/_projects.html.haml
@@ -1,10 +1,10 @@
 .panel.panel-default.projects-list-holder
   .panel-heading.clearfix
     .input-group
-      = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control'
+      = search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
       - if can? current_user, :create_projects, @group
         %span.input-group-btn
           = link_to new_project_path(namespace_id: @group.id), class: 'btn btn-green' do
             New project
 
-  = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false
+  = render 'shared/projects/list', projects: @projects, projects_limit: 20, stars: false, skip_namespace: true
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 3a6d07ebddf956a3ea051334396d458e8a898d9a..fee4b0052b590af4f5c3f62d65717275c7e120eb 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -12,7 +12,7 @@
 .clearfix.js-toggle-container
   = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form'  do
     .form-group
-      = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
+      = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false }
     = button_tag 'Search', class: 'btn'
 
   - if current_user && current_user.can?(:admin_group_member, @group)
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index a9ba9d2ba101a31bf1ad716ec1586d314aa8281a..dc8e81323a63daf43f7e527162142042b3a37111 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -25,11 +25,9 @@
         .hidden-xs
           - if current_user
             = render "events/event_last_push", event: @last_push
-
-            %ul.nav.nav-pills.event_filter.pull-right
-              %li
-                = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'rss-btn' do
-                  %i.fa.fa-rss
+            .pull-right
+              = link_to group_path(@group, { format: :atom, private_token: current_user.private_token }), title: "Feed", class: 'btn rss-btn' do
+                %i.fa.fa-rss
 
             = render 'shared/event_filter'
             %hr
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 2468687b56d8c26894e9bc91b749b0b3a3a30f6d..1a883e20e899dea4f3dd4b7e7b3536b137ef6741 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -6,7 +6,7 @@
         = brand_header_logo
         .gitlab-text-container
           %h3 GitLab
-          
+
     - if defined?(sidebar) && sidebar
       = render "layouts/nav/#{sidebar}"
     - elsif current_user
@@ -23,6 +23,7 @@
           = current_user.username
   .content-wrapper
     = render "layouts/flash"
+    = yield :flash_message
     %div{ class: container_class }
       .content
         .clearfix
diff --git a/app/views/layouts/_search.html.haml b/app/views/layouts/_search.html.haml
index e2d2dec7ab814d3d98df01734db0e8bb998e9dd8..ceb64ce3157f69fd054ece96b2fcfac9f66ea4fc 100644
--- a/app/views/layouts/_search.html.haml
+++ b/app/views/layouts/_search.html.haml
@@ -1,6 +1,6 @@
 .search
   = form_tag search_path, method: :get, class: 'navbar-form pull-left' do |f|
-    = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control"
+    = search_field_tag "search", nil, placeholder: search_placeholder, class: "search-input form-control", spellcheck: false
     = hidden_field_tag :group_id, @group.try(:id)
     - if @project && @project.persisted?
       = hidden_field_tag :project_id, @project.id
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 1261f6254d7a016b69ef1bc447245c4ef2a1c0f9..c2683bc62194754b8818c46d9839c7da6ff71b01 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,10 +1,9 @@
 = render 'projects/last_push'
 .gray-content-block.activity-filter-block
   - if current_user
-    %ul.nav.nav-pills.event_filter.pull-right
-      %li
-        = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'rss-btn' do
-          %i.fa.fa-rss
+    .pull-right
+      = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'btn rss-btn' do
+        %i.fa.fa-rss
 
   = render 'shared/event_filter'
 .content_list{:"data-href" => activity_project_path(@project)}
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index b4c7d8b9b71112435caefe74b49a388c3a5c9628..a1ae1397584ea63494c8f788c02d32181c88114a 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -19,7 +19,7 @@
   - blob_commit = @repository.last_commit_for_path(@commit.id, blob.path)
   = render blob_commit, project: @project
 
-%div#tree-content-holder.tree-content-holder
+%div#blob-content-holder.blob-content-holder
   %article.file-holder
     .file-title
       = blob_icon blob.mode, blob.name
diff --git a/app/views/projects/builds/_build.html.haml b/app/views/projects/builds/_build.html.haml
deleted file mode 100644
index 65fd9413b60727ae180025e897d8246a653855bd..0000000000000000000000000000000000000000
--- a/app/views/projects/builds/_build.html.haml
+++ /dev/null
@@ -1,50 +0,0 @@
-- gl_project = build.project.gl_project
-%tr.build
-  %td.status
-    = ci_status_with_icon(build.status)
-
-  %td.build-link
-    = link_to namespace_project_build_path(gl_project.namespace, gl_project, build) do
-      %strong Build ##{build.id}
-
-  - if defined?(ref)
-    %td
-      = build.ref
-
-  %td
-    = build.stage
-
-  %td
-    = build.name
-    .pull-right
-      - if build.tags.any?
-        - build.tag_list.each do |tag|
-          %span.label.label-primary
-            = tag
-      - if build.trigger_request
-        %span.label.label-info triggered
-      - if build.allow_failure
-        %span.label.label-danger allowed to fail
-
-  %td.duration
-    - if build.duration
-      #{duration_in_words(build.finished_at, build.started_at)}
-
-  %td.timestamp
-    - if build.finished_at
-      %span #{time_ago_in_words build.finished_at} ago
-
-  - if build.project.coverage_enabled?
-    %td.coverage
-      - if build.coverage
-        #{build.coverage}%
-
-  %td
-    - if defined?(controls) && current_user && can?(current_user, :manage_builds, gl_project)
-      .pull-right
-        - if build.active?
-          = link_to cancel_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), title: 'Cancel build' do
-            %i.fa.fa-remove.cred
-        - elsif build.commands.present?
-          = link_to retry_namespace_project_build_path(gl_project.namespace, gl_project, build, return_to: request.original_url), method: :post, title: 'Retry build' do
-            %i.fa.fa-repeat
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index b561078e8c7e1c83de79aa14fba92410b3bf7651..9c3ae622b728bd28f51fc63c991f67b1b47b4b39 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -9,7 +9,7 @@
   #up-build-trace
   - if @commit.matrix_for_ref?(@build.ref)
     %ul.center-top-menu.build-top-menu
-      - @commit.builds_without_retry_for_ref(@build.ref).each do |build|
+      - @commit.latest_builds_for_ref(@build.ref).each do |build|
         %li{class: ('active' if build == @build) }
           = link_to namespace_project_build_path(@project.namespace, @project, build) do
             = ci_icon_for_status(build.status)
@@ -20,7 +20,7 @@
                 = build.id
 
 
-      - unless @commit.builds_without_retry_for_ref(@build.ref).include?(@build)
+      - unless @commit.latest_builds_for_ref(@build.ref).include?(@build)
         %li.active
           %a
             Build ##{@build.id}
diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml
index 4b69a6d7a6f2c478a4b8b2df0ef8bf2d313973d5..3bc2daeec4ed10dee40e8255083bb4c0669e4b78 100644
--- a/app/views/projects/buttons/_notifications.html.haml
+++ b/app/views/projects/buttons/_notifications.html.haml
@@ -1,6 +1,6 @@
 - return unless @membership
 
-= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline-form', id: 'notification-form' do
+= form_tag profile_notifications_path, method: :put, remote: true, class: 'inline', id: 'notification-form' do
   = hidden_field_tag :notification_type, 'project'
   = hidden_field_tag :notification_id, @membership.id
   = hidden_field_tag :notification_level
diff --git a/app/views/projects/commit/ci.html.haml b/app/views/projects/commit/ci.html.haml
index 26ab38445c2a4360933dd6aa9bd7c715e48609a4..4a1ef378a30e5cc7d0314d9c16ecbed35129efec 100644
--- a/app/views/projects/commit/ci.html.haml
+++ b/app/views/projects/commit/ci.html.haml
@@ -20,30 +20,31 @@
   .bs-callout.bs-callout-warning
     \.gitlab-ci.yml not found in this commit
 
-- @ci_commit.refs.each do |ref|
+.gray-content-block.second-block
+  Latest builds
+  - if @ci_commit.duration > 0
+    %small.pull-right
+      %i.fa.fa-time
+      #{time_interval_in_words @ci_commit.duration}
+
+%table.table.builds
+  %thead
+    %tr
+      %th Status
+      %th Build ID
+      %th Ref
+      %th Stage
+      %th Name
+      %th Duration
+      %th Finished at
+      - if @ci_project && @ci_project.coverage_enabled?
+        %th Coverage
+      %th
+  - @ci_commit.refs.each do |ref|
+    = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.statuses.for_ref(ref).latest.ordered, coverage: @ci_project.try(:coverage_enabled?), controls: true
+
+- if @ci_commit.retried.any?
   .gray-content-block.second-block
-    Builds for #{ref}
-    - if @ci_commit.duration_for_ref(ref) > 0
-      %small.pull-right
-        %i.fa.fa-time
-        #{time_interval_in_words @ci_commit.duration_for_ref(ref)}
-
-  %table.table.builds
-    %thead
-      %tr
-        %th Status
-        %th Build ID
-        %th Stage
-        %th Name
-        %th Duration
-        %th Finished at
-        - if @ci_project && @ci_project.coverage_enabled?
-          %th Coverage
-        %th
-    = render partial: "projects/builds/build", collection: @ci_commit.builds_without_retry.for_ref(ref), controls: true
-
-- if @ci_commit.retried_builds.any?
-  %h3
     Retried builds
 
   %table.table.builds
@@ -59,4 +60,4 @@
         - if @ci_project && @ci_project.coverage_enabled?
           %th Coverage
         %th
-    = render partial: "projects/builds/build", collection: @ci_commit.retried_builds, ref: true
+    = render partial: "projects/commit_statuses/commit_status", collection: @ci_commit.retried, coverage: @ci_project.try(:coverage_enabled?)
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..e3a17faf0bd947a8d2fbefaf8bb0fa6a8d10fec0
--- /dev/null
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -0,0 +1,51 @@
+%tr.commit_status
+  %td.status
+    = ci_status_with_icon(commit_status.status)
+
+  %td.commit_status-link
+    - if commit_status.target_url
+      = link_to commit_status.target_url do
+        %strong Build ##{commit_status.id}
+    - else
+      %strong Build ##{commit_status.id}
+
+  %td
+    = commit_status.ref
+
+  %td
+    = commit_status.stage
+
+  %td
+    = commit_status.name
+    .pull-right
+      - if commit_status.tags.any?
+        - commit_status.tags.each do |tag|
+          %span.label.label-primary
+            = tag
+      - if commit_status.try(:trigger_request)
+        %span.label.label-info triggered
+      - if commit_status.try(:allow_failure)
+        %span.label.label-danger allowed to fail
+
+  %td.duration
+    - if commit_status.duration
+      #{duration_in_words(commit_status.finished_at, commit_status.started_at)}
+
+  %td.timestamp
+    - if commit_status.finished_at
+      %span #{time_ago_in_words commit_status.finished_at} ago
+
+  - if defined?(coverage) && coverage
+    %td.coverage
+      - if commit_status.try(:coverage)
+        #{commit_status.coverage}%
+
+  %td
+    - if defined?(controls) && controls && current_user && can?(current_user, :manage_builds, gl_project)
+      .pull-right
+        - if commit_status.cancel_url
+          = link_to commit_status.cancel_url, title: 'Cancel' do
+            %i.fa.fa-remove.cred
+        - elsif commit_status.retry_url
+          = link_to commit_status.retry_url, method: :post, title: 'Retry' do
+            %i.fa.fa-repeat
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index 1b093c8f51400bf66d6446ed91bb751fd75c8c66..daab2326bc7615d88c6a43ed6ae4b8f5fac58397 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -111,10 +111,10 @@
 
         - if current_user.can_create_group?
           .pull-right
-            .light.in-line
+            .light.inline
               .space-right
                 Need a group for several dependent projects?
-            = link_to new_group_path, class: "btn btn-xs" do
+            = link_to new_group_path, class: "btn" do
               Create a group
 
 .save-project-loader.hide
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 9a0a824b811ab3a01a094b769e56401fe6ce7d06..82809bec5b81f49754c11d384e0e26e491b96e34 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -5,7 +5,7 @@
   .clearfix.js-toggle-container
     = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form'  do
       .form-group
-        = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input' }
+        = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control search-text-input', spellcheck: false }
       = button_tag 'Search', class: 'btn'
 
     - if can?(current_user, :admin_project_member, @project)
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index b9486a9b49265c92771b58c529b26fd9f51cb4de..07c24950ee238e89c7d5744d79ea54bd79d5db6d 100644
--- a/app/views/projects/repositories/_download_archive.html.haml
+++ b/app/views/projects/repositories/_download_archive.html.haml
@@ -3,10 +3,10 @@
 - split_button = split_button || false
 - if split_button == true
   %span.btn-group{class: btn_class}
-    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do
+    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn btn-success col-xs-10', rel: 'nofollow' do
       %i.fa.fa-download
       %span Download zip
-    %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+    %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
       %span.caret
       %span.sr-only
         Select Archive Format
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index efa119edd5a36d086d345758dfd608a34c7e244f..e95d987d74c90f25a3bcb00acde0f7a1c03801ea 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -2,9 +2,10 @@
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
 
-- if current_user && can?(current_user, :download_code, @project)
-  = render 'shared/no_ssh'
-  = render 'shared/no_password'
+= content_for :flash_message do
+  - if current_user && can?(current_user, :download_code, @project)
+    = render 'shared/no_ssh'
+    = render 'shared/no_password'
 
 - if prefer_readme?
   = render 'projects/last_push'
diff --git a/app/views/projects/tree/_readme.html.haml b/app/views/projects/tree/_readme.html.haml
index f082d71186558744fc0eba13427e81659c39c845..7e9af19c8ba015e3acf3efd511cca0ee0bce42d2 100644
--- a/app/views/projects/tree/_readme.html.haml
+++ b/app/views/projects/tree/_readme.html.haml
@@ -1,7 +1,8 @@
-%article.readme-holder#README
-  = link_to '#README' do
-    %h4.readme-file-title
-      %i.fa.fa-file
-      = readme.name
-  .wiki
+%article.file-holder.readme-holder#README
+  .file-title
+    = link_to '#README' do
+      %strong
+        %i.fa.fa-file
+        = readme.name
+  .file-content.wiki
     = render_readme(readme)
diff --git a/app/views/projects/tree/_tree.html.haml b/app/views/projects/tree/_tree.html.haml
index 457f8a4a585a577e4874b75fe4f36065cbeb7a58..7ff48e32e601e8a349ad3dc1d1d1e63b9b34c1ed 100644
--- a/app/views/projects/tree/_tree.html.haml
+++ b/app/views/projects/tree/_tree.html.haml
@@ -1,59 +1,61 @@
-%ul.breadcrumb.repo-breadcrumb
-  %li
-    = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
-      = @project.path
-  - tree_breadcrumbs(tree, 6) do |title, path|
+.gray-content-block
+  %ul.breadcrumb.repo-breadcrumb
     %li
-      - if path
-        = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
-      - else
-        = link_to title, '#'
-  - if allowed_tree_edit?
-    %li
-      %span.dropdown
-        %a.dropdown-toggle.btn.btn-xs.add-to-tree{href: '#', "data-toggle" => "dropdown"}
-          = icon('plus')
-        %ul.dropdown-menu
-          %li
-            = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
-              = icon('pencil fw')
-              Create file
-          %li
-            = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
-              = icon('file fw')
-              Upload file
-          %li.divider
-          %li
-            = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
-              = icon('folder fw')
-              New directory
+      = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
+        = @project.path
+    - tree_breadcrumbs(tree, 6) do |title, path|
+      %li
+        - if path
+          = link_to truncate(title, length: 40), namespace_project_tree_path(@project.namespace, @project, path)
+        - else
+          = link_to title, '#'
+    - if allowed_tree_edit?
+      %li
+        %span.dropdown
+          %a.dropdown-toggle.btn.add-to-tree{href: '#', "data-toggle" => "dropdown"}
+            = icon('plus')
+          %ul.dropdown-menu
+            %li
+              = link_to namespace_project_new_blob_path(@project.namespace, @project, @id), title: 'Create file', id: 'new-file-link' do
+                = icon('pencil fw')
+                Create file
+            %li
+              = link_to '#modal-upload-blob', { 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal'} do
+                = icon('file fw')
+                Upload file
+            %li.divider
+            %li
+              = link_to '#modal-create-new-dir', { 'data-target' => '#modal-create-new-dir', 'data-toggle' => 'modal'} do
+                = icon('folder fw')
+                New directory
 
-%div#tree-content-holder.tree-content-holder.prepend-top-20
-  %table#tree-slider{class: "table_#{@hex_path} tree-table" }
-    %thead
-      %tr
-        %th Name
-        %th Last Update
-        %th.hidden-xs
-          .pull-left Last Commit
-          .last-commit.hidden-sm.pull-left
-            &nbsp;
-            %i.fa.fa-angle-right
-            &nbsp;
-            %small.light
-              = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
-              &ndash;
-              = truncate(@commit.title, length: 50)
-          = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
+%div#tree-content-holder.tree-content-holder
+  .tree-table-holder
+    %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
+      %thead
+        %tr
+          %th Name
+          %th Last Update
+          %th.hidden-xs
+            .pull-left Last Commit
+            .last-commit.hidden-sm.pull-left
+              &nbsp;
+              %i.fa.fa-angle-right
+              &nbsp;
+              %small.light
+                = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
+                &ndash;
+                = truncate(@commit.title, length: 50)
+            = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
 
-    - if @path.present?
-      %tr.tree-item
-        %td.tree-item-file-name
-          = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
-        %td
-        %td.hidden-xs
+      - if @path.present?
+        %tr.tree-item
+          %td.tree-item-file-name
+            = link_to "..", namespace_project_tree_path(@project.namespace, @project, up_dir_path), class: 'prepend-left-10'
+          %td
+          %td.hidden-xs
 
-    = render_tree(tree)
+      = render_tree(tree)
 
   - if tree.readme
     = render "projects/tree/readme", readme: tree.readme
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 03e6a522b251ae1b422f1bcc2b4d5d504d74af04..d179a1abec1323380c4de50cc3cdb40f7a1f46da 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -3,6 +3,7 @@
 
 = render 'nav'
 .gray-content-block
+  = render 'main_links'
   %h3.page-title
     All Pages
 %ul.content-list
diff --git a/app/views/search/_form.html.haml b/app/views/search/_form.html.haml
index 3938c545caddd0bbbc7948b22026c83fbc3fc835..17b0981f073917fb08e66a8b8260d0f72d9ca41f 100644
--- a/app/views/search/_form.html.haml
+++ b/app/views/search/_form.html.haml
@@ -6,7 +6,7 @@
 
   .search-holder.clearfix
     .input-group
-      = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true
+      = search_field_tag :search, params[:search], placeholder: "Search for projects, issues etc", class: "form-control search-text-input", id: "dashboard_search", autofocus: true, spellcheck: false
       %span.input-group-btn
         = button_tag 'Search', class: "btn btn-primary"
     - unless params[:snippets].eql? 'true'
diff --git a/app/views/shared/issuable/_search_form.html.haml b/app/views/shared/issuable/_search_form.html.haml
index 58c3de64b77727b4a6fe2d1d494e2e01f39535b3..3a5ad00aa91a7c63b045bab87a6b292a5f296ea9 100644
--- a/app/views/shared/issuable/_search_form.html.haml
+++ b/app/views/shared/issuable/_search_form.html.haml
@@ -1,6 +1,6 @@
 = form_tag(path, method: :get, id: "issue_search_form", class: 'pull-left issue-search-form') do
   .append-right-10.hidden-xs.hidden-sm
-    = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input' }
+    = search_field_tag :issue_search, params[:issue_search], { placeholder: 'Filter by title or description', class: 'form-control issue_search search-text-input', spellcheck: false }
     = hidden_field_tag :state, params['state']
     = hidden_field_tag :scope, params['scope']
     = hidden_field_tag :assignee_id, params['assignee_id']
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 16e1d8421ded3c7fcb7d5fa406e3014481f2d880..357cfd6a37007542ca262155848ece0f66d85b06 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -2,11 +2,12 @@
 - avatar = true unless local_assigns[:avatar] == false
 - stars = true unless local_assigns[:stars] == false
 - ci = false unless local_assigns[:ci] == true
+- skip_namespace = false unless local_assigns[:skip_namespace] == true
 
 %ul.projects-list
   - projects.each_with_index do |project, i|
     - css_class = (i >= projects_limit) ? 'hide' : nil
-    = render "shared/projects/project", project: project,
+    = render "shared/projects/project", project: project, skip_namespace: skip_namespace,
       avatar: avatar, stars: stars, css_class: css_class, ci: ci
 
   - if projects.size > projects_limit
diff --git a/app/views/shared/projects/_project.html.haml b/app/views/shared/projects/_project.html.haml
index e67e5a8a6387c21381e86d60a8498b0de703ecd3..aee839b44e7ee43d15749ec3215142c606f56a4b 100644
--- a/app/views/shared/projects/_project.html.haml
+++ b/app/views/shared/projects/_project.html.haml
@@ -1,6 +1,7 @@
 - avatar = true unless local_assigns[:avatar] == false
 - stars = true unless local_assigns[:stars] == false
 - ci = false unless local_assigns[:ci] == true
+- skip_namespace = false unless local_assigns[:skip_namespace] == true
 - css_class = '' unless local_assigns[:css_class]
 - css_class += " no-description" unless project.description.present?
 %li.project-row{ class: css_class }
@@ -11,7 +12,7 @@
           = project_icon(project, alt: '', class: 'avatar project-avatar s46')
       %span.project-full-name
         %span.namespace-name
-          - if project.namespace
+          - if project.namespace && !skip_namespace
             = project.namespace.human_name
             \/
         %span.project-name.filter-title
diff --git a/config/mail_room.yml.example b/config/mail_room.yml.example
index ed4a5193a6a85879615b93472ae903fd230a891b..bb624e8a187d4bccdafabd11994d4c6b02463712 100644
--- a/config/mail_room.yml.example
+++ b/config/mail_room.yml.example
@@ -12,8 +12,10 @@
     # :email: "gitlab-incoming@gmail.com"
     # # Email account password
     # :password: "password"
+
     # # The name of the mailbox where incoming mail will end up. Usually "inbox".
     # :name: "inbox"
+
     # # Always "sidekiq".
     # :delivery_method: sidekiq
     # # Always true.
@@ -25,5 +27,13 @@
     #   :namespace: resque:gitlab
     #   # Always "incoming_email".
     #   :queue: incoming_email
-    #   # Always "EmailReceiverWorker"
+    #   # Always "EmailReceiverWorker".
     #   :worker: EmailReceiverWorker
+
+    # # Always "redis".
+    # :arbitration_method: redis
+    # :arbitration_options:
+    #   # The URL to the Redis server. Should match the URL in config/resque.yml.
+    #   :redis_url: redis://localhost:6379
+    #   # Always "mail_room:gitlab".
+    #   :namespace: mail_room:gitlab
diff --git a/db/migrate/20151008123042_add_type_and_description_to_builds.rb b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c72b1c611c6db7125921b5f2aebe8f3aa884661e
--- /dev/null
+++ b/db/migrate/20151008123042_add_type_and_description_to_builds.rb
@@ -0,0 +1,9 @@
+class AddTypeAndDescriptionToBuilds < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :type, :string
+    add_column :ci_builds, :target_url, :string
+    add_column :ci_builds, :description, :string
+    add_index :ci_builds, [:commit_id, :type, :ref]
+    add_index :ci_builds, [:commit_id, :type, :name, :ref]
+  end
+end
diff --git a/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f5c44babd84277d0b7764942a114c54f7e1b32fb
--- /dev/null
+++ b/db/migrate/20151008130321_migrate_name_to_description_for_builds.rb
@@ -0,0 +1,5 @@
+class MigrateNameToDescriptionForBuilds < ActiveRecord::Migration
+  def change
+    execute("UPDATE ci_builds SET type='Ci::Build' WHERE type IS NULL")
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index c5c462c2e5700976f156f9a5883d72b5813a84fd..7a11dfca03422fcc090afbc39ef469dff12191dd 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20151007120511) do
+ActiveRecord::Schema.define(version: 20151008130321) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -103,9 +103,14 @@ ActiveRecord::Schema.define(version: 20151007120511) do
     t.boolean  "tag"
     t.string   "ref"
     t.integer  "user_id"
+    t.string   "type"
+    t.string   "target_url"
+    t.string   "description"
   end
 
   add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
+  add_index "ci_builds", ["commit_id", "type", "name", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_name_and_ref", using: :btree
+  add_index "ci_builds", ["commit_id", "type", "ref"], name: "index_ci_builds_on_commit_id_and_type_and_ref", using: :btree
   add_index "ci_builds", ["commit_id"], name: "index_ci_builds_on_commit_id", using: :btree
   add_index "ci_builds", ["project_id", "commit_id"], name: "index_ci_builds_on_project_id_and_commit_id", using: :btree
   add_index "ci_builds", ["project_id"], name: "index_ci_builds_on_project_id", using: :btree
diff --git a/doc/api/commits.md b/doc/api/commits.md
index eb8d6a43592a0f2210317c59719fac951e9ca36a..9f72adc6ed9ac22a6233ecb4d7e0bb30465f6bac 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -62,7 +62,8 @@ Parameters:
   "authored_date": "2012-09-20T09:06:12+03:00",
   "parent_ids": [
     "ae1d9fb46aa2b07ee9836d49862ec4e2c46fbbba"
-  ]
+  ],
+  "status": "running"
 }
 ```
 
@@ -156,3 +157,84 @@ Parameters:
   "line_type": "new"
 }
 ```
+
+## Get the status of a commit
+
+Get the statuses of a commit in a project.
+
+```
+GET /projects/:id/repository/commits/:sha/statuses
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `sha` (required) - The commit SHA
+- `ref` (optional) - Filter by ref name, it can be branch or tag
+- `stage` (optional) - Filter by stage
+- `name` (optional) - Filer by status name, eg. jenkins
+- `all` (optional) - The flag to return all statuses, not only latest ones
+
+```json
+[
+  {
+    "id": 13,
+    "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27",
+    "ref": "test",
+    "status": "success",
+    "name": "ci/jenkins",
+    "target_url": "http://jenkins/project/url",
+    "description": "Jenkins success",
+    "created_at": "2015-10-12T09:47:16.250Z",
+    "started_at": "2015-10-12T09:47:16.250Z"",
+    "finished_at": "2015-10-12T09:47:16.262Z",
+    "author": {
+      "id": 1,
+      "username": "admin",
+      "email": "admin@local.host",
+      "name": "Administrator",
+      "blocked": false,
+      "created_at": "2012-04-29T08:46:00Z"
+    }
+  }
+]
+```
+
+## Post the status to commit
+
+Adds or updates a status of a commit.
+
+```
+POST /projects/:id/statuses/:sha
+```
+
+- `id` (required) - The ID of a project
+- `sha` (required) - The commit SHA
+- `state` (required) - The state of the status. Can be: pending, running, success, failed, canceled
+- `ref` (optional) - The ref (branch or tag) to which the status refers
+- `name` or `context` (optional) - The label to differentiate this status from the status of other systems. Default: "default"
+- `target_url` (optional) - The target URL to associate with this status
+- `description` (optional) - The short description of the status
+
+```json
+{
+  "id": 13,
+  "sha": "b0b3a907f41409829b307a28b82fdbd552ee5a27",
+  "ref": "test",
+  "status": "success",
+  "name": "ci/jenkins",
+  "target_url": "http://jenkins/project/url",
+  "description": "Jenkins success",
+  "created_at": "2015-10-12T09:47:16.250Z",
+  "started_at": "2015-10-12T09:47:16.250Z"",
+  "finished_at": "2015-10-12T09:47:16.262Z",
+  "author": {
+    "id": 1,
+    "username": "admin",
+    "email": "admin@local.host",
+    "name": "Administrator",
+    "blocked": false,
+    "created_at": "2012-04-29T08:46:00Z"
+  }
+}
+```
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index bb551fc67f751dd7029b50416fb0d7e54ac313d6..ffa7f2cdf14d388d6c1bfaff2e3d73863168c73a 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -188,6 +188,7 @@ Parameters:
 - `title` (required)             - Title of MR
 - `description` (optional)       - Description of MR
 - `target_project_id` (optional) - The target project (numeric id)
+- `labels` (optional)            - Labels for MR as a comma-separated list
 
 ```json
 {
@@ -239,6 +240,7 @@ Parameters:
 - `title`                       - Title of MR
 - `description`                 - Description of MR
 - `state_event`                 - New state (close|reopen|merge)
+- `labels` (optional)           - Labels for MR as a comma-separated list
 
 ```json
 {
diff --git a/doc/customization/welcome_message.md b/doc/customization/welcome_message.md
index 6c141d1fb7a1ef39481e11943d6ae98f979aa983..e993230bb8806a109187a73a5f6a92906a4b2356 100644
--- a/doc/customization/welcome_message.md
+++ b/doc/customization/welcome_message.md
@@ -8,31 +8,5 @@ It is possible to add a markdown-formatted welcome message to your GitLab
 sign-in page. Users of GitLab Enterprise Edition should use the [branded login
 page feature](/ee/customization/branded_login_page.html) instead.
 
-## Omnibus-gitlab example
-
-In `/etc/gitlab/gitlab.rb`:
-
-```ruby
-gitlab_rails['extra_sign_in_text'] = <<'EOS'
-# ACME GitLab
-Welcome to the [ACME](http://www.example.com) GitLab server!
-EOS
-```
-
-Run `sudo gitlab-ctl reconfigure` for changes to take effect.
-
-## Installation from source
-
-In `/home/git/gitlab/config/gitlab.yml`:
-
-```yaml
-# snip
-production:
-  # snip
-  extra:
-    sign_in_text: |
-      # ACME GitLab
-      Welcome to the [ACME](http://www.example.com) GitLab server!
-```      
-
-Run `sudo service gitlab reload` for the change to take effect.
+The welcome message (extra_sign_in_text) can now be set/changed in the Admin UI.  
+Admin area > Settings
\ No newline at end of file
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index 872674234718def43048a9881996f271685c198b..aafa2345fab1506d275beb30270f5cf1d1d757a9 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -2,10 +2,6 @@
 
 GitLab can be set up to allow users to comment on issues and merge requests by replying to notification emails.
 
-**Warning**: Do not enable Reply by email if you have **multiple GitLab application servers**. 
-Due to an issue with the way incoming emails are read from the mail server, every incoming reply-by-email email will result in as many comments being created as you have application servers.
-[A fix is being worked on.](https://github.com/tpitale/mail_room/issues/46)
-
 ## Get a mailbox
 
 Reply by email requires an IMAP-enabled email account, with a provider or server that supports [email sub-addressing](https://en.wikipedia.org/wiki/Email_address#Sub-addressing). Sub-addressing is a feature where any email to `user+some_arbitrary_tag@example.com` will end up in the mailbox for `user@example.com`, and is supported by providers such as Gmail, Yahoo! Mail, Outlook.com and iCloud, as well as the Postfix mail server which you can run on-premises.
@@ -118,8 +114,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
         :email: "incoming"
         # Email account password
         :password: "[REDACTED]"
+
         # The name of the mailbox where incoming mail will end up. Usually "inbox".
         :name: "inbox"
+
         # Always "sidekiq".
         :delivery_method: sidekiq
         # Always true.
@@ -133,6 +131,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
           :queue: incoming_email
           # Always "EmailReceiverWorker"
           :worker: EmailReceiverWorker
+
+        # Always "redis".
+        :arbitration_method: redis
+        :arbitration_options:
+          # The URL to the Redis server. Should match the URL in config/resque.yml.
+          :redis_url: redis://localhost:6379
+          # Always "mail_room:gitlab".
+          :namespace: mail_room:gitlab
     ```
 
     ```yaml
@@ -151,8 +157,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
         :email: "gitlab-incoming@gmail.com"
         # Email account password
         :password: "[REDACTED]"
+
         # The name of the mailbox where incoming mail will end up. Usually "inbox".
         :name: "inbox"
+
         # Always "sidekiq".
         :delivery_method: sidekiq
         # Always true.
@@ -166,6 +174,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
           :queue: incoming_email
           # Always "EmailReceiverWorker"
           :worker: EmailReceiverWorker
+
+        # Always "redis".
+        :arbitration_method: redis
+        :arbitration_options:
+          # The URL to the Redis server. Should match the URL in config/resque.yml.
+          :redis_url: redis://localhost:6379
+          # Always "mail_room:gitlab".
+          :namespace: mail_room:gitlab
     ```
 
 5. Edit the init script configuration at `/etc/default/gitlab` to enable `mail_room`:
@@ -228,8 +244,10 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
         :email: "gitlab-incoming@gmail.com"
         # Email account password
         :password: "[REDACTED]"
+
         # The name of the mailbox where incoming mail will end up. Usually "inbox".
         :name: "inbox"
+
         # Always "sidekiq".
         :delivery_method: sidekiq
         # Always true.
@@ -243,6 +261,14 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
           :queue: incoming_email
           # Always "EmailReceiverWorker"
           :worker: EmailReceiverWorker
+
+        # Always "redis".
+        :arbitration_method: redis
+        :arbitration_options:
+          # The URL to the Redis server. Should match the URL in config/resque.yml.
+          :redis_url: redis://localhost:6379
+          # Always "mail_room:gitlab".
+          :namespace: mail_room:gitlab
     ```
 
 4. Uncomment the `mail_room` line in your `Procfile`:
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index a66a863f6c45e215c6b9878baa69c3291c1d5c52..da719229ab6854dabac1dfce7deff1fa39b1e3d9 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -23,9 +23,11 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
 cd /home/git/gitlab
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- Gemfile.lock db/schema.rb
-LATEST_TAG=$(git describe --tags `git rev-list --tags --max-count=1`)
-sudo -u git -H git checkout $LATEST_TAG -b $LATEST_TAG
+sudo -u git -H git checkout LATEST_TAG -b LATEST_TAG
 ```
+Replace `LATEST_TAG` with the latest GitLab tag you want to update to, for example `v8.0.3`.  
+Use `git tag -l 'v*.[0-9]' --sort='v:refname'` to see a list of all tags.  
+Make sure to update patch versions only (check your current version with `cat VERSION`)
 
 ### 3. Update gitlab-shell to the corresponding version
 
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index c185ccfcac31f4a70efd37b108fa825249900b72..ef99a69f60a81bb21e5553cc7bee73cce5ab092a 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -314,7 +314,8 @@ X-Gitlab-Event: Note Hook
         "name": "John Smith",
         "email": "john@example.com"
       }
-    }
+    },
+    "work_in_progress": false
   }
 }
 ```
@@ -500,6 +501,7 @@ X-Gitlab-Event: Merge Request Hook
         "email": "gitlabdev@dv6700.(none)"
       }
     },
+    "work_in_progress": false,
     "url": "http://example.com/diaspora/merge_requests/1",
     "action": "open"
   }
@@ -537,4 +539,4 @@ When you press 'Test Hook' in GitLab, you should see something like this in the
 {"before":"077a85dd266e6f3573ef7e9ef8ce3343ad659c4e","after":"95cd4a99e93bc4bbabacfa2cd10e6725b1403c60",<SNIP>}
 example.com - - [14/May/2014:07:45:26 EDT] "POST / HTTP/1.1" 200 0
 - -> /
-```
\ No newline at end of file
+```
diff --git a/features/steps/project/commits/commits.rb b/features/steps/project/commits/commits.rb
index ae5f90004e6cc7e07cd7e55c0812a21fc93f3cd5..a3cb83880e30b00de8c88219a71d77632698d981 100644
--- a/features/steps/project/commits/commits.rb
+++ b/features/steps/project/commits/commits.rb
@@ -118,6 +118,6 @@ class Spinach::Features::ProjectCommits < Spinach::FeatureSteps
 
   step 'I see builds list' do
     expect(page).to have_content "build: pending"
-    expect(page).to have_content "Builds for master"
+    expect(page).to have_content "Latest builds"
   end
 end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index c09488d3547713f815e9c236f18fe36e909b5aa9..afc0402f9e1d3462ba0b800f1171aed3b087dfce 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -46,6 +46,7 @@ module API
     mount Services
     mount Files
     mount Commits
+    mount CommitStatus
     mount Namespaces
     mount Branches
     mount Labels
diff --git a/lib/api/commit_statuses.rb b/lib/api/commit_statuses.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2c0596c9dfb64cf730fada8ac4bf16157677294d
--- /dev/null
+++ b/lib/api/commit_statuses.rb
@@ -0,0 +1,80 @@
+require 'mime/types'
+
+module API
+  # Project commit statuses API
+  class CommitStatus < Grape::API
+    resource :projects do
+      before { authenticate! }
+
+      # Get a commit's statuses
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   sha (required) - The commit hash
+      #   ref (optional) - The ref
+      #   stage (optional) - The stage
+      #   name (optional) - The name
+      #   all (optional) - Show all statuses, default: false
+      # Examples:
+      #   GET /projects/:id/repository/commits/:sha/statuses
+      get ':id/repository/commits/:sha/statuses' do
+        authorize! :read_commit_statuses, user_project
+        sha = params[:sha]
+        ci_commit = user_project.ci_commit(sha)
+        not_found! 'Commit' unless ci_commit
+        statuses = ci_commit.statuses
+        statuses = statuses.latest unless parse_boolean(params[:all])
+        statuses = statuses.where(ref: params[:ref]) if params[:ref].present?
+        statuses = statuses.where(stage: params[:stage]) if params[:stage].present?
+        statuses = statuses.where(name: params[:name]) if params[:name].present?
+        present paginate(statuses), with: Entities::CommitStatus
+      end
+
+      # Post status to commit
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   sha (required) - The commit hash
+      #   ref (optional) - The ref
+      #   state (required) - The state of the status. Can be: pending, running, success, error or failure
+      #   target_url (optional) - The target URL to associate with this status
+      #   description (optional) - A short description of the status
+      #   name or context (optional) - A string label to differentiate this status from the status of other systems. Default: "default"
+      # Examples:
+      #   POST /projects/:id/statuses/:sha
+      post ':id/statuses/:sha' do
+        authorize! :create_commit_status, user_project
+        required_attributes! [:state]
+        attrs = attributes_for_keys [:ref, :target_url, :description, :context, :name]
+        commit = @project.commit(params[:sha])
+        not_found! 'Commit' unless commit
+
+        ci_commit = @project.ensure_ci_commit(commit.sha)
+
+        name = params[:name] || params[:context]
+        status = GenericCommitStatus.running_or_pending.find_by(commit: ci_commit, name: name, ref: params[:ref])
+        status ||= GenericCommitStatus.new(commit: ci_commit, user: current_user)
+        status.update(attrs)
+
+        case params[:state].to_s
+        when 'running'
+          status.run
+        when 'success'
+          status.success
+        when 'failed'
+          status.drop
+        when 'canceled'
+          status.cancel
+        else
+          status.status = params[:state].to_s
+        end
+
+        if status.save
+          present status, with: Entities::CommitStatus
+        else
+          render_validation_error!(status)
+        end
+      end
+    end
+  end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 9620d36ac4170e459e67ff4458741e1b9fce2fc4..519072d015791bc083e2b621adbf4668df13227b 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -149,6 +149,7 @@ module API
 
     class RepoCommitDetail < RepoCommit
       expose :parent_ids, :committed_date, :authored_date
+      expose :status
     end
 
     class ProjectSnippet < Grape::Entity
@@ -228,6 +229,12 @@ module API
       expose :created_at
     end
 
+    class CommitStatus < Grape::Entity
+      expose :id, :sha, :ref, :status, :name, :target_url, :description,
+             :created_at, :started_at, :finished_at
+      expose :author, using: Entities::UserBasic
+    end
+
     class Event < Grape::Entity
       expose :title, :project_id, :action_name
       expose :target_id, :target_type, :author_id
diff --git a/lib/api/merge_requests.rb b/lib/api/merge_requests.rb
index 63ea2f054386730e65afd4cbe2ec6e2a0afa33aa..f3a59fadf24c12b8581aafa6d3af827c49993317 100644
--- a/lib/api/merge_requests.rb
+++ b/lib/api/merge_requests.rb
@@ -99,7 +99,7 @@ module API
       #   id (required)            - The ID of a project - this will be the source of the merge request
       #   source_branch (required) - The source branch
       #   target_branch (required) - The target branch
-      #   target_project           - The target project of the merge request defaults to the :id of the project
+      #   target_project_id        - The target project of the merge request defaults to the :id of the project
       #   assignee_id              - Assignee user ID
       #   title (required)         - Title of MR
       #   description              - Description of MR
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index f47bc1236b818fdbe14e1a9edb0fe1d03a54f23d..b80c0b8b2735427e5703f2175cb939c03e4b1a26 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -2,7 +2,7 @@ module Ci
   module API
     module Entities
       class Commit < Grape::Entity
-        expose :id, :ref, :sha, :project_id, :before_sha, :created_at
+        expose :id, :sha, :project_id, :created_at
         expose :status, :finished_at, :duration
         expose :git_commit_message, :git_author_name, :git_author_email
       end
@@ -12,7 +12,7 @@ module Ci
       end
 
       class Build < Grape::Entity
-        expose :id, :commands, :ref, :sha, :project_id, :repo_url,
+        expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
           :before_sha, :allow_git_fetch, :project_name
 
         expose :options do |model|
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index 1ea7751e27dd0b7f9ec0a59e29a202a2972a5718..4be99dd88c29dd87ba829973da60aeca464dbf2d 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -35,7 +35,7 @@ module Gitlab
       end
 
       def find_by_email
-        ::User.find_by(email: auth_hash.email)
+        ::User.find_by(email: auth_hash.email.downcase)
       end
 
       def update_user_attributes
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index 21b582afba4648b177e0699ff21248a81b67ba6b..2fcd70182b9765414d1fc6c3dd2ab1c2c5ef5de8 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -27,6 +27,7 @@
 
 FactoryGirl.define do
   factory :ci_build, class: Ci::Build do
+    name 'test'
     ref 'master'
     tag false
     started_at 'Di 29. Okt 09:51:28 CET 2013'
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
new file mode 100644
index 0000000000000000000000000000000000000000..52de437052d99e4047900e64560c5771a06afb13
--- /dev/null
+++ b/spec/factories/commit_statuses.rb
@@ -0,0 +1,15 @@
+FactoryGirl.define do
+  factory :commit_status, class: CommitStatus do
+    started_at 'Di 29. Okt 09:51:28 CET 2013'
+    finished_at 'Di 29. Okt 09:53:28 CET 2013'
+    name 'default'
+    status 'success'
+    description 'commit status'
+    commit factory: :ci_commit
+
+    factory :generic_commit_status, class: GenericCommitStatus do
+      name 'generic'
+      description 'external commit status'
+    end
+  end
+end
diff --git a/spec/features/commits_spec.rb b/spec/features/commits_spec.rb
index 5da220859e37af91b52ae8142679fb9a805ebac4..cbb6360069b8ffec309dbb9580662934651640bb 100644
--- a/spec/features/commits_spec.rb
+++ b/spec/features/commits_spec.rb
@@ -12,6 +12,7 @@ describe "Commits" do
       @ci_project = project.ensure_gitlab_ci_project
       @commit = FactoryGirl.create :ci_commit, gl_project: project, sha: project.commit.sha
       @build = FactoryGirl.create :ci_build, commit: @commit
+      @generic_status = FactoryGirl.create :generic_commit_status, commit: @commit
     end
 
     before do
diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb
index fb70a36dc02f84078badbf26d0ae618d8a3178fc..0c8d06b7059bed24926fad705e09c5d8a3299049 100644
--- a/spec/helpers/labels_helper_spec.rb
+++ b/spec/helpers/labels_helper_spec.rb
@@ -14,11 +14,6 @@ describe LabelsHelper do
         expect(label).not_to receive(:project)
         link_to_label(label)
       end
-
-      it 'includes option for "No Label"' do
-        result = project_labels_options(project)
-        expect(result).to include('No Label')
-      end
     end
 
     context 'without @project set' do
diff --git a/spec/javascripts/fixtures/line_highlighter.html.haml b/spec/javascripts/fixtures/line_highlighter.html.haml
index da1ebcdb23cacb744c7824b612e5c496f15ce086..514877340e4f2ba38b55037cc92318798c0c003e 100644
--- a/spec/javascripts/fixtures/line_highlighter.html.haml
+++ b/spec/javascripts/fixtures/line_highlighter.html.haml
@@ -1,4 +1,4 @@
-#tree-content-holder
+#blob-content-holder
   .file-content
     .line-numbers
       - 1.upto(25) do |i|
diff --git a/spec/javascripts/line_highlighter_spec.js.coffee b/spec/javascripts/line_highlighter_spec.js.coffee
index 57453c716a5df44f2e7df4268beab377a993d9fb..a073f21e7bcbe563f53bab788be747dbf9fea754 100644
--- a/spec/javascripts/line_highlighter_spec.js.coffee
+++ b/spec/javascripts/line_highlighter_spec.js.coffee
@@ -39,7 +39,7 @@ describe 'LineHighlighter', ->
       expect(spy).toHaveBeenPrevented()
 
     it 'handles garbage input from the hash', ->
-      func = -> new LineHighlighter('#tree-content-holder')
+      func = -> new LineHighlighter('#blob-content-holder')
       expect(func).not.toThrow()
 
   describe '#clickHandler', ->
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index fd2e5f6d0e170df0dc56f347b2f99d61fb3b04e6..b5b56a349526774d7f5dd98194a4e1d7092d3844 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -13,6 +13,17 @@ describe Gitlab::LDAP::User do
   let(:auth_hash) do
     OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info)
   end
+  let(:ldap_user_upper_case) { Gitlab::LDAP::User.new(auth_hash_upper_case) }
+  let(:info_upper_case) do
+    {
+      name: 'John',
+      email: 'John@Example.com', # Email address has upper case chars
+      nickname: 'john'
+    }
+  end
+  let(:auth_hash_upper_case) do
+    OmniAuth::AuthHash.new(uid: 'my-uid', provider: 'ldapmain', info: info_upper_case)
+  end
 
   describe :changed? do
     it "marks existing ldap user as changed" do
@@ -57,6 +68,16 @@ describe Gitlab::LDAP::User do
       expect(existing_user.id).to eql ldap_user.gl_user.id
     end
 
+    it 'connects to existing ldap user if the extern_uid changes and email address has upper case characters' do
+      existing_user = create(:omniauth_user, email: 'john@example.com', extern_uid: 'old-uid', provider: 'ldapmain')
+      expect{ ldap_user_upper_case.save }.not_to change{ User.count }
+
+      existing_user.reload
+      expect(existing_user.ldap_identity.extern_uid).to eql 'my-uid'
+      expect(existing_user.ldap_identity.provider).to eql 'ldapmain'
+      expect(existing_user.id).to eql ldap_user.gl_user.id
+    end
+
     it 'maintains an identity per provider' do
       existing_user = create(:omniauth_user, email: 'john@example.com', provider: 'twitter')
       expect(existing_user.identities.count).to eql(1)
diff --git a/spec/models/ci/build_spec.rb b/spec/models/build_spec.rb
similarity index 74%
rename from spec/models/ci/build_spec.rb
rename to spec/models/build_spec.rb
index da56f6e31ae11673498d682be19e8903c8881735..d875015b9913b2c259a2dc49484a99f9c44e9222 100644
--- a/spec/models/ci/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -30,17 +30,9 @@ describe Ci::Build do
   let(:gl_project) { FactoryGirl.create :empty_project, gitlab_ci_project: project }
   let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
   let(:build) { FactoryGirl.create :ci_build, commit: commit }
-  subject { build }
 
-  it { is_expected.to belong_to(:commit) }
-  it { is_expected.to belong_to(:user) }
-  it { is_expected.to validate_presence_of :status }
   it { is_expected.to validate_presence_of :ref }
 
-  it { is_expected.to respond_to :success? }
-  it { is_expected.to respond_to :failed? }
-  it { is_expected.to respond_to :running? }
-  it { is_expected.to respond_to :pending? }
   it { is_expected.to respond_to :trace_html }
 
   describe :first_pending do
@@ -67,72 +59,6 @@ describe Ci::Build do
     end
   end
 
-  describe :started? do
-    subject { build.started? }
-
-    context 'without started_at' do
-      before { build.started_at = nil }
-
-      it { is_expected.to be_falsey }
-    end
-
-    %w(running success failed).each do |status|
-      context "if build status is #{status}" do
-        before { build.status = status }
-
-        it { is_expected.to be_truthy }
-      end
-    end
-
-    %w(pending canceled).each do |status|
-      context "if build status is #{status}" do
-        before { build.status = status }
-
-        it { is_expected.to be_falsey }
-      end
-    end
-  end
-
-  describe :active? do
-    subject { build.active? }
-
-    %w(pending running).each do |state|
-      context "if build.status is #{state}" do
-        before { build.status = state }
-
-        it { is_expected.to be_truthy }
-      end
-    end
-
-    %w(success failed canceled).each do |state|
-      context "if build.status is #{state}" do
-        before { build.status = state }
-
-        it { is_expected.to be_falsey }
-      end
-    end
-  end
-
-  describe :complete? do
-    subject { build.complete? }
-
-    %w(success failed canceled).each do |state|
-      context "if build.status is #{state}" do
-        before { build.status = state }
-
-        it { is_expected.to be_truthy }
-      end
-    end
-
-    %w(pending running).each do |state|
-      context "if build.status is #{state}" do
-        before { build.status = state }
-
-        it { is_expected.to be_falsey }
-      end
-    end
-  end
-
   describe :ignored? do
     subject { build.ignored? }
 
@@ -200,31 +126,6 @@ describe Ci::Build do
     it { is_expected.to eq(commit.project.timeout) }
   end
 
-  describe :duration do
-    subject { build.duration }
-
-    it { is_expected.to eq(120.0) }
-
-    context 'if the building process has not started yet' do
-      before do
-        build.started_at = nil
-        build.finished_at = nil
-      end
-
-      it { is_expected.to be_nil }
-    end
-
-    context 'if the building process has started' do
-      before do
-        build.started_at = Time.now - 1.minute
-        build.finished_at = nil
-      end
-
-      it { is_expected.to be_a(Float) }
-      it { is_expected.to be > 0.0 }
-    end
-  end
-
   describe :options do
     let(:options) do
       {
@@ -239,18 +140,6 @@ describe Ci::Build do
     it { is_expected.to eq(options) }
   end
 
-  describe :sha do
-    subject { build.sha }
-
-    it { is_expected.to eq(commit.sha) }
-  end
-
-  describe :short_sha do
-    subject { build.short_sha }
-
-    it { is_expected.to eq(commit.short_sha) }
-  end
-
   describe :allow_git_fetch do
     subject { build.allow_git_fetch }
 
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index acff1ddf0fcf274a1fef5112d3a2275d1dfb0f54..330971174fbb38c4afb11dcf080d8fd5f3478c77 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -23,6 +23,8 @@ describe Ci::Commit do
   let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
 
   it { is_expected.to belong_to(:gl_project) }
+  it { is_expected.to have_many(:statuses) }
+  it { is_expected.to have_many(:trigger_requests) }
   it { is_expected.to have_many(:builds) }
   it { is_expected.to validate_presence_of :sha }
 
@@ -47,10 +49,12 @@ describe Ci::Commit do
       @second = FactoryGirl.create :ci_build, commit: commit
     end
 
-    it "creates new build" do
+    it "creates only a new build" do
       expect(commit.builds.count(:all)).to eq 2
+      expect(commit.statuses.count(:all)).to eq 2
       commit.retry
       expect(commit.builds.count(:all)).to eq 3
+      expect(commit.statuses.count(:all)).to eq 3
     end
   end
 
@@ -78,8 +82,8 @@ describe Ci::Commit do
     subject { commit.stage }
 
     before do
-      @second = FactoryGirl.create :ci_build, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: :pending
-      @first = FactoryGirl.create :ci_build, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: :pending
+      @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending'
+      @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending'
     end
 
     it 'returns first running stage' do
@@ -88,7 +92,7 @@ describe Ci::Commit do
 
     context 'first build succeeded' do
       before do
-        @first.update_attributes(status: :success)
+        @first.success
       end
 
       it 'returns last running stage' do
@@ -98,8 +102,8 @@ describe Ci::Commit do
 
     context 'all builds succeeded' do
       before do
-        @first.update_attributes(status: :success)
-        @second.update_attributes(status: :success)
+        @first.success
+        @second.success
       end
 
       it 'returns nil' do
@@ -111,6 +115,33 @@ describe Ci::Commit do
   describe :create_next_builds do
   end
 
+  describe :refs do
+    subject { commit.refs }
+
+    before do
+      FactoryGirl.create :commit_status, commit: commit, name: 'deploy'
+      FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop'
+      FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master'
+    end
+
+    it 'returns all refs' do
+      is_expected.to contain_exactly('master', 'develop', nil)
+    end
+  end
+
+  describe :retried do
+    subject { commit.retried }
+
+    before do
+      @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
+      @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
+    end
+
+    it 'returns old builds' do
+      is_expected.to contain_exactly(@commit1)
+    end
+  end
+
   describe :create_builds do
     let(:commit) { FactoryGirl.create :ci_commit, gl_project: gl_project }
 
@@ -194,9 +225,10 @@ describe Ci::Commit do
         it 'rebuilds commit' do
           expect(commit.status).to eq('skipped')
           expect(create_builds(trigger_request)).to be_truthy
-          commit.builds.reload
-          expect(commit.builds.size).to eq(2)
-          expect(commit.status).to eq('pending')
+
+          # since everything in Ci::Commit is cached we need to fetch a new object
+          new_commit = Ci::Commit.find_by_id(commit.id)
+          expect(new_commit.status).to eq('pending')
         end
       end
     end
@@ -252,10 +284,10 @@ describe Ci::Commit do
 
   describe :should_create_next_builds? do
     before do
-      @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: :success
-      @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: :failed
-      @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: :failed
-      @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :success
+      @build1 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: false, status: 'success'
+      @build2 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'develop', tag: false, status: 'failed'
+      @build3 = FactoryGirl.create :ci_build, commit: commit, name: 'build1', ref: 'master', tag: true, status: 'failed'
+      @build4 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'success'
     end
 
     context 'for success' do
@@ -266,7 +298,7 @@ describe Ci::Commit do
 
     context 'for failed' do
       before do
-        @build4.update_attributes(status: :failed)
+        @build4.update_attributes(status: 'failed')
       end
 
       it 'to not create' do
@@ -286,7 +318,7 @@ describe Ci::Commit do
 
     context 'for running' do
       before do
-        @build4.update_attributes(status: :running)
+        @build4.update_attributes(status: 'running')
       end
 
       it 'to not create' do
@@ -296,7 +328,7 @@ describe Ci::Commit do
 
     context 'for retried' do
       before do
-        @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: :failed
+        @build5 = FactoryGirl.create :ci_build, commit: commit, name: 'build4', ref: 'master', tag: false, status: 'failed'
       end
 
       it 'to not create' do
diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
index 04e870dce7f5d36778f61afb7736fc9825153789..d9b3d34ff1529bd04343f399b4b2fef6aac60a35 100644
--- a/spec/models/ci/project_services/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -35,7 +35,7 @@ describe Ci::MailService do
       let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true) }
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -58,7 +58,7 @@ describe Ci::MailService do
       let(:project) { FactoryGirl.create(:ci_project, email_add_pusher: true, email_only_broken_builds: false) }
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -86,7 +86,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -115,7 +115,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -144,7 +144,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :success, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'success', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
@@ -167,7 +167,7 @@ describe Ci::MailService do
       end
       let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
       let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
-      let(:build) { FactoryGirl.create(:ci_build, status: :failed, commit: commit, user: user) }
+      let(:build) { FactoryGirl.create(:ci_build, status: 'failed', commit: commit, user: user) }
 
       before do
         allow(mail).to receive_messages(
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c96a606fdaab60445d7415e85763049530672317
--- /dev/null
+++ b/spec/models/commit_status_spec.rb
@@ -0,0 +1,164 @@
+require 'spec_helper'
+
+describe CommitStatus do
+  let(:commit) { FactoryGirl.create :ci_commit }
+  let(:commit_status) { FactoryGirl.create :commit_status, commit: commit }
+
+  it { is_expected.to belong_to(:commit) }
+  it { is_expected.to belong_to(:user) }
+  it { is_expected.to validate_presence_of(:name) }
+  it { is_expected.to validate_inclusion_of(:status).in_array(%w(pending running failed success canceled)) }
+
+  it { is_expected.to delegate_method(:sha).to(:commit) }
+  it { is_expected.to delegate_method(:short_sha).to(:commit) }
+  it { is_expected.to delegate_method(:gl_project).to(:commit) }
+  
+  it { is_expected.to respond_to :success? }
+  it { is_expected.to respond_to :failed? }
+  it { is_expected.to respond_to :running? }
+  it { is_expected.to respond_to :pending? }
+
+  describe :author do
+    subject { commit_status.author }
+    before { commit_status.author = User.new }
+
+    it { is_expected.to eq(commit_status.user) }
+  end
+
+  describe :started? do
+    subject { commit_status.started? }
+
+    context 'without started_at' do
+      before { commit_status.started_at = nil }
+
+      it { is_expected.to be_falsey }
+    end
+
+    %w(running success failed).each do |status|
+      context "if commit status is #{status}" do
+        before { commit_status.status = status }
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
+    %w(pending canceled).each do |status|
+      context "if commit status is #{status}" do
+        before { commit_status.status = status }
+
+        it { is_expected.to be_falsey }
+      end
+    end
+  end
+
+  describe :active? do
+    subject { commit_status.active? }
+
+    %w(pending running).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { commit_status.status = state }
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
+    %w(success failed canceled).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { commit_status.status = state }
+
+        it { is_expected.to be_falsey }
+      end
+    end
+  end
+
+  describe :complete? do
+    subject { commit_status.complete? }
+
+    %w(success failed canceled).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { commit_status.status = state }
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
+    %w(pending running).each do |state|
+      context "if commit_status.status is #{state}" do
+        before { commit_status.status = state }
+
+        it { is_expected.to be_falsey }
+      end
+    end
+  end
+
+  describe :duration do
+    subject { commit_status.duration }
+
+    it { is_expected.to eq(120.0) }
+
+    context 'if the building process has not started yet' do
+      before do
+        commit_status.started_at = nil
+        commit_status.finished_at = nil
+      end
+
+      it { is_expected.to be_nil }
+    end
+
+    context 'if the building process has started' do
+      before do
+        commit_status.started_at = Time.now - 1.minute
+        commit_status.finished_at = nil
+      end
+
+      it { is_expected.to be_a(Float) }
+      it { is_expected.to be > 0.0 }
+    end
+  end
+  
+  describe :latest do
+    subject { CommitStatus.latest.order(:id) }
+
+    before do
+      @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+      @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+      @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'cc', status: 'success'
+      @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'bb', status: 'success'
+      @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'success'
+    end
+
+    it 'return unique statuses' do
+      is_expected.to eq([@commit2, @commit3, @commit4, @commit5])
+    end
+  end
+
+  describe :for_ref do
+    subject { CommitStatus.for_ref('bb').order(:id) }
+
+    before do
+      @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+      @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+      @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
+    end
+
+    it 'return statuses with equal and nil ref set' do
+      is_expected.to eq([@commit1])
+    end
+  end
+
+  describe :running_or_pending do
+    subject { CommitStatus.running_or_pending.order(:id) }
+
+    before do
+      @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running'
+      @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending'
+      @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success'
+      @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed'
+      @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled'
+    end
+
+    it 'return statuses that are running or pending' do
+      is_expected.to eq([@commit1, @commit2])
+    end
+  end
+end
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f442fa5fbe5f608b80f68dcddca4d215575e23b3
--- /dev/null
+++ b/spec/models/generic_commit_status_spec.rb
@@ -0,0 +1,39 @@
+require 'spec_helper'
+
+describe GenericCommitStatus do
+  let(:commit) { FactoryGirl.create :ci_commit }
+  let(:generic_commit_status) { FactoryGirl.create :generic_commit_status, commit: commit }
+
+  describe :context do
+    subject { generic_commit_status.context }
+    before { generic_commit_status.context = 'my_context' }
+
+    it { is_expected.to eq(generic_commit_status.name) }
+  end
+
+  describe :tags do
+    subject { generic_commit_status.tags }
+
+    it { is_expected.to eq([:external]) }
+  end
+
+  describe :set_default_values do
+    before do
+      generic_commit_status.context = nil
+      generic_commit_status.stage = nil
+      generic_commit_status.save
+    end
+
+    describe :context do
+      subject { generic_commit_status.context }
+
+      it { is_expected.to_not be_nil }
+    end
+
+    describe :stage do
+      subject { generic_commit_status.stage }
+
+      it { is_expected.to_not be_nil }
+    end
+  end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index 17a49013d2515bb4f1e7349808c0990b4a57219b..6aaf1c036b05aa231ed9a7fdd9a64865b1f80d9a 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -165,6 +165,17 @@ describe MergeRequest do
     end
   end
 
+  describe "#hook_attrs" do
+    it "has all the required keys" do
+      attrs = subject.hook_attrs
+      attrs = attrs.to_h
+      expect(attrs).to include(:source)
+      expect(attrs).to include(:target)
+      expect(attrs).to include(:last_commit)
+      expect(attrs).to include(:work_in_progress)
+    end
+  end
+
   it_behaves_like 'an editable mentionable' do
     subject { create(:merge_request) }
 
diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f8a3493f52df63b0c050b7da3f6d2a936273c9a5
--- /dev/null
+++ b/spec/models/project_services/bamboo_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id                    :integer          not null, primary key
+#  type                  :string(255)
+#  title                 :string(255)
+#  project_id            :integer
+#  created_at            :datetime
+#  updated_at            :datetime
+#  active                :boolean          default(FALSE), not null
+#  properties            :text
+#  template              :boolean          default(FALSE)
+#  push_events           :boolean          default(TRUE)
+#  issues_events         :boolean          default(TRUE)
+#  merge_requests_events :boolean          default(TRUE)
+#  tag_push_events       :boolean          default(TRUE)
+#  note_events           :boolean          default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe BambooService, models: true do
+  describe "Associations" do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe "Execute" do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project) }
+
+    before do
+      @bamboo_service = BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "reset password if url changed" do
+      @bamboo_service.bamboo_url = 'http://gitlab1.com'
+      @bamboo_service.save
+      expect(@bamboo_service.password).to be_nil
+    end
+
+    it "does not reset password if username changed" do
+      @bamboo_service.username = "some_name"
+      @bamboo_service.save
+      expect(@bamboo_service.password).to eq("password")
+    end
+  end
+end
diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3dbd2346bccd0eff9966e16382efa19f8f72f1ab
--- /dev/null
+++ b/spec/models/project_services/teamcity_service_spec.rb
@@ -0,0 +1,56 @@
+# == Schema Information
+#
+# Table name: services
+#
+#  id                    :integer          not null, primary key
+#  type                  :string(255)
+#  title                 :string(255)
+#  project_id            :integer
+#  created_at            :datetime
+#  updated_at            :datetime
+#  active                :boolean          default(FALSE), not null
+#  properties            :text
+#  template              :boolean          default(FALSE)
+#  push_events           :boolean          default(TRUE)
+#  issues_events         :boolean          default(TRUE)
+#  merge_requests_events :boolean          default(TRUE)
+#  tag_push_events       :boolean          default(TRUE)
+#  note_events           :boolean          default(TRUE), not null
+#
+
+require 'spec_helper'
+
+describe TeamcityService, models: true do
+  describe "Associations" do
+    it { is_expected.to belong_to :project }
+    it { is_expected.to have_one :service_hook }
+  end
+
+  describe "Execute" do
+    let(:user)    { create(:user) }
+    let(:project) { create(:project) }
+
+    before do
+      @teamcity_service = TeamcityService.create(
+        project: create(:project),
+        properties: {
+          teamcity_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "reset password if url changed" do
+      @teamcity_service.teamcity_url = 'http://gitlab1.com'
+      @teamcity_service.save
+      expect(@teamcity_service.password).to be_nil
+    end
+
+    it "does not reset password if username changed" do
+      @teamcity_service.username = "some_name"
+      @teamcity_service.save
+      expect(@teamcity_service.password).to eq("password")
+    end
+  end
+end
diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb
index a213ffe6c4b33bbdda75fb6f9d6a44fe33f3de4a..da87ea5b84f175ce7835d6708021cf03558bf10d 100644
--- a/spec/models/service_spec.rb
+++ b/spec/models/service_spec.rb
@@ -103,4 +103,27 @@ describe Service do
       end
     end
   end
+
+  describe "#prop_updated?" do
+    let(:service) do
+      BambooService.create(
+        project: create(:project),
+        properties: {
+          bamboo_url: 'http://gitlab.com',
+          username: 'mic',
+          password: "password"
+        }
+      )
+    end
+
+    it "returns false" do
+      service.username = "key_changed"
+      expect(service.prop_updated?(:bamboo_url)).to be_falsy
+    end
+
+    it "returns true" do
+      service.bamboo_url = "http://other.com"
+      expect(service.prop_updated?(:bamboo_url)).to be_truthy
+    end
+  end
 end
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b9e6dfc15a796c5ac5b6db2bd0c343ef11e5d594
--- /dev/null
+++ b/spec/requests/api/commit_status_spec.rb
@@ -0,0 +1,135 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+  include ApiHelpers
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:reporter) { create(:project_member, user: user, project: project, access_level: ProjectMember::REPORTER) }
+  let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
+  let(:commit) { project.repository.commit }
+  let!(:ci_commit) { project.ensure_ci_commit(commit.id) }
+  let(:commit_status) { create(:commit_status, commit: ci_commit) }
+
+  describe "GET /projects/:id/repository/commits/:sha/statuses" do
+    context "reporter user" do
+      let(:statuses_id) { json_response.map { |status| status['id'] } }
+
+      before do
+        @status1 = create(:commit_status, commit: ci_commit, status: 'running')
+        @status2 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'pending')
+        @status3 = create(:commit_status, commit: ci_commit, name: 'coverage', ref: 'develop', status: 'running')
+        @status4 = create(:commit_status, commit: ci_commit, name: 'coverage', status: 'success')
+        @status5 = create(:commit_status, commit: ci_commit, ref: 'develop', status: 'success')
+        @status6 = create(:commit_status, commit: ci_commit, status: 'success')
+      end
+
+      it "should return latest commit statuses" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user)
+        expect(response.status).to eq(200)
+
+        expect(json_response).to be_an Array
+        expect(statuses_id).to contain_exactly(@status3.id, @status4.id, @status5.id, @status6.id)
+      end
+
+      it "should return all commit statuses" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?all=1", user)
+        expect(response.status).to eq(200)
+
+        expect(json_response).to be_an Array
+        expect(statuses_id).to contain_exactly(@status1.id, @status2.id, @status3.id, @status4.id, @status5.id, @status6.id)
+      end
+
+      it "should return latest commit statuses for specific ref" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?ref=develop", user)
+        expect(response.status).to eq(200)
+
+        expect(json_response).to be_an Array
+        expect(statuses_id).to contain_exactly(@status3.id, @status5.id)
+      end
+
+      it "should return latest commit statuses for specific name" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses?name=coverage", user)
+        expect(response.status).to eq(200)
+
+        expect(json_response).to be_an Array
+        expect(statuses_id).to contain_exactly(@status3.id, @status4.id)
+      end
+    end
+
+    context "guest user" do
+      it "should not return project commits" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user2)
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context "unauthorized user" do
+      it "should not return project commits" do
+        get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses")
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/statuses/:sha' do
+    let(:post_url) { "/projects/#{project.id}/statuses/#{commit.id}" }
+
+    context 'reporter user' do
+      context 'should create commit status' do
+        it 'with only required parameters' do
+          post api(post_url, user), state: 'success'
+          expect(response.status).to eq(201)
+          expect(json_response['sha']).to eq(commit.id)
+          expect(json_response['status']).to eq('success')
+          expect(json_response['name']).to eq('default')
+          expect(json_response['ref']).to be_nil
+          expect(json_response['target_url']).to be_nil
+          expect(json_response['description']).to be_nil
+        end
+
+        it 'with all optional parameters' do
+          post api(post_url, user), state: 'success', context: 'coverage', ref: 'develop', target_url: 'url', description: 'test'
+          expect(response.status).to eq(201)
+          expect(json_response['sha']).to eq(commit.id)
+          expect(json_response['status']).to eq('success')
+          expect(json_response['name']).to eq('coverage')
+          expect(json_response['ref']).to eq('develop')
+          expect(json_response['target_url']).to eq('url')
+          expect(json_response['description']).to eq('test')
+        end
+      end
+
+      context 'should not create commit status' do
+        it 'with invalid state' do
+          post api(post_url, user), state: 'invalid'
+          expect(response.status).to eq(400)
+        end
+
+        it 'without state' do
+          post api(post_url, user)
+          expect(response.status).to eq(400)
+        end
+
+        it 'invalid commit' do
+          post api("/projects/#{project.id}/statuses/invalid_sha", user), state: 'running'
+          expect(response.status).to eq(404)
+        end
+      end
+    end
+
+    context 'guest user' do
+      it 'should not create commit status' do
+        post api(post_url, user2)
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not create commit status' do
+        post api(post_url)
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+end
diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb
index a1c248c636eb4fa699777f8accd71e6fb84a30f2..49acc3368f43a536b972df9e8015e117d82db02e 100644
--- a/spec/requests/api/commits_spec.rb
+++ b/spec/requests/api/commits_spec.rb
@@ -47,6 +47,19 @@ describe API::API, api: true  do
         get api("/projects/#{project.id}/repository/commits/invalid_sha", user)
         expect(response.status).to eq(404)
       end
+
+      it "should return not_found for CI status" do
+        get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+        expect(response.status).to eq(200)
+        expect(json_response['status']).to eq('not_found')
+      end
+
+      it "should return status for CI" do
+        ci_commit = project.ensure_ci_commit(project.repository.commit.sha)
+        get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user)
+        expect(response.status).to eq(200)
+        expect(json_response['status']).to eq(ci_commit.status)
+      end
     end
 
     context "unauthorized user" do