diff --git a/.gitignore b/.gitignore
index 39ff95c50ee64fa21bb7bf6dc9d28126da616d71..f5b6427ca033d60c0b69d5ce1d1c9c81eda22b2b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ rails_best_practices_output.html
 tmp/
 vendor/bundle/*
 builds/*
+shared/*
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 128faa07db8420421daa85e3b3bf31c55998393c..e8290fb36b28e8638d5ce493f026f1e4c8853c6a 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -80,7 +80,6 @@ flog:
   tags:
     - ruby
     - mysql
-  allow_failure: true
 
 flay:
   script:
@@ -88,4 +87,12 @@ flay:
   tags:
     - ruby
     - mysql
+
+bundler:audit:
+  script: 
+    - "bundle exec bundle-audit update"
+    - "bundle exec bundle-audit check"
+  tags:
+    - ruby
+    - mysql
   allow_failure: true
diff --git a/.rubocop.yml b/.rubocop.yml
index 11e4502849a2f909983a4f05b9e8ff6c3f2437ad..d59edbc8b17b6ca14bd9e64cd0b0e9c0b6b19f11 100644
--- a/.rubocop.yml
+++ b/.rubocop.yml
@@ -888,7 +888,7 @@ Lint/RequireParentheses:
 Lint/RescueException:
   Description: 'Avoid rescuing the Exception class.'
   StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues'
-  Enabled: false
+  Enabled: true
 
 Lint/ShadowingOuterLocalVariable:
   Description: >-
diff --git a/.ruby-version b/.ruby-version
index 399088bf465606fc2257b2741338f95e9c790390..04b10b4f150135c42748aead459c7ae6caa77c16 100644
--- a/.ruby-version
+++ b/.ruby-version
@@ -1 +1 @@
-2.1.6
+2.1.7
diff --git a/CHANGELOG b/CHANGELOG
index c0257cc8a2f22a59c22b63d4dc8641e77460061e..a37e248f9c2f1a99ca7b93e64f6854eb03a29cd3 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,12 +1,41 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
-v 8.2.0 (unreleased)
-  - Remove CSS property preventing hard tabs from rendering in Chromium 45 (Stan Hu)
+v 8.3.0 (unreleased)
+  - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
+  - Fix 500 error when update group member permission
+  - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
+  - Add ignore whitespace change option to commit view
+  - Fire update hook from GitLab
+  - Don't show project fork event as "imported"
+
+v 8.2.2
+  - Fix 404 in redirection after removing a project (Stan Hu)
+  - Ensure cached application settings are refreshed at startup (Stan Hu)
+  - Fix Error 500 when viewing user's personal projects from admin page (Stan Hu)
+  - Fix: Raw private snippets access workflow
+  - Prevent "413 Request entity too large" errors when pushing large files with LFS
+  - Fix invalid links within projects dashboard header
+
+v 8.2.1
+  - Forcefully update builds that didn't want to update with state machine
+  - Fix: saving GitLabCiService as Admin Template
+
+v 8.2.0
+  - Improved performance of finding projects and groups in various places
+  - Improved performance of rendering user profile pages and Atom feeds
+  - Expose build artifacts path as config option
+  - Fix grouping of contributors by email in graph.
+  - Improved performance of finding issues with/without labels
   - Fix Drone CI service template not saving properly (Stan Hu)
+  - Fix avatars not showing in Atom feeds and project issues when Gravatar disabled (Stan Hu)
   - Added a GitLab specific profiling tool called "Sherlock" (see GitLab CE merge request #1749)
   - Upgrade gitlab_git to 7.2.20 and rugged to 0.23.3 (Stan Hu)
   - Improved performance of finding users by one of their Email addresses
   - Add allow_failure field to commit status API (Stan Hu)
+  - Commits without .gitlab-ci.yml are marked as skipped
+  - Save detailed error when YAML syntax is invalid
+  - Since GitLab CI is enabled by default, remove enabling it by pushing .gitlab-ci.yml
+  - Added build artifacts
   - Improved performance of replacing references in comments
   - Show last project commit to default branch on project home page
   - Highlight comment based on anchor in URL
@@ -20,21 +49,37 @@ v 8.2.0 (unreleased)
   - Send build name and stage in CI notification e-mail
   - Extend yml syntax for only and except to support specifying repository path
   - Enable shared runners to all new projects
+  - Bump GitLab-Workhorse to 0.4.1
+  - Allow to define cache in `.gitlab-ci.yml`
   - Fix: 500 error returned if destroy request without HTTP referer (Kazuki Shimizu)
   - Remove deprecated CI events from project settings page
   - Use issue editor as cross reference comment author when issue is edited with a new mention.
   - Add graphs of commits ahead and behind default branch (Jeff Stubler)
+  - Improve personal snippet access workflow (Douglas Alexandre)
   - [API] Add ability to fetch the commit ID of the last commit that actually touched a file
+  - Fix omniauth documentation setting for omnibus configuration (Jon Cairns)
   - Add "New file" link to dropdown on project page
   - Include commit logs in project search
   - Add "added", "modified" and "removed" properties to commit object in webhook
   - Rename "Back to" links to "Go to" because its not always a case it point to place user come from
   - Allow groups to appear in the search results if the group owner allows it
+  - Add email notification to former assignee upon unassignment (Adam Lieskovsk媒)
   - New design for project graphs page
+  - Remove deprecated dumped yaml file generated from previous job definitions
   - Fix incoming email config defaults
+  - Show specific runners from projects where user is master or owner
   - MR target branch is now visible on a list view when it is different from project's default one
   - Improve Continuous Integration graphs page
   - Make color of "Accept Merge Request" button consistent with current build status
+  - Add ignore white space option in merge request diff and commit and compare view
+  - Ability to add release notes (markdown text and attachments) to git tags (aka Releases)
+  - Relative links from a repositories README.md now link to the default branch
+  - Fix trailing whitespace issue in merge request/issue title
+  - Fix bug when milestone/label filter was empty for dashboard issues page
+  - Add ability to create milestone in group projects from single form
+  - Add option to create merge request when editing/creating a file (Dirceu Tiegs)
+  - Prevent the last owner of a group from being able to delete themselves by 'adding' themselves as a master (James Lopez)
+  - Add Award Emoji to issue and merge request pages
 
 v 8.1.4
   - Fix bug where manually merged branches in a MR would end up with an empty diff (Stan Hu)
@@ -104,7 +149,6 @@ v 8.1.0
   - Show CI status on Your projects page and Starred projects page
   - Remove "Continuous Integration" page from dashboard
   - Add notes and SSL verification entries to hook APIs (Ben Boeckel)
-  - Added build artifacts
   - Fix grammar in admin area "labels" .nothing-here-block when no labels exist.
   - Move CI runners page to project settings area
   - Move CI variables page to project settings area
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9f79ff413a07f0bb181e3b8e9eb4ea162f9ba760..7f5da063fd49460a34be2d4d322244a8507ca09e 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -10,7 +10,7 @@ By submitting code as an individual you agree to the [individual contributor lic
 
 ## Security vulnerability disclosure
 
-Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
+Please report suspected security vulnerabilities in private to support@gitlab.com, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
 
 ## Closing policy for issues and merge requests
 
@@ -23,10 +23,20 @@ Issues and merge requests should be in English and contain appropriate language
 ## Helping others
 
 Please help other GitLab users when you can.
-The channnels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/).
-Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the irc channel.
+The channels people will reach out on can be found on the [getting help page](https://about.gitlab.com/getting-help/).
+Sign up for the mailinglist, answer GitLab questions on StackOverflow or respond in the IRC channel.
 You can also sign up on [CodeTriage](http://www.codetriage.com/gitlabhq/gitlabhq) to help with one issue every day.
 
+## I want to contribute!
+
+If you want to contribute to GitLab, but are not sure where to start,
+look for [issues](https://gitlab.com/gitlab-org/gitlab-ce/issues?milestone_id=&scope=all&sort=created_desc&state=opened&utf8=%E2%9C%93&assignee_id=&author_id=&milestone_title=&label_name=up-for-grabs)
+with the label `up-for-grabs`.
+These issues will be of reasonable size and challenge, for anyone to start
+contributing to GitLab.
+
+This was inspired by [an article by Kent C. Dodds](https://medium.com/@kentcdodds/first-timers-only-78281ea47455#.i2f363mx4).
+
 ## Issue tracker
 
 To get support for your particular problem please use the [getting help channels](https://about.gitlab.com/getting-help/).
@@ -35,7 +45,7 @@ The [GitLab CE issue tracker on GitLab.com](https://gitlab.com/gitlab-org/gitlab
 
 Do not use the issue tracker for feature requests. We have a specific [feature request forum](http://feedback.gitlab.com) for this purpose. Please keep feature requests as small and simple as possible, complex ones might be edited to make them small and simple.
 
-Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](http://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
+Please send a merge request with a tested solution or a merge request with a failing test instead of opening an issue if you can. If you're unsure where to post, post to the [mailing list](https://groups.google.com/forum/#!forum/gitlabhq) or [Stack Overflow](https://stackoverflow.com/questions/tagged/gitlab) first. There are a lot of helpful GitLab users there who may be able to help you quickly. If your particular issue turns out to be a bug, it will find its way from there.
 
 ### Issue tracker guidelines
 
@@ -47,10 +57,10 @@ Please send a merge request with a tested solution or a merge request with a fai
 1. **Observed behavior**
 1. **Relevant logs and/or screenshots:** Please use code blocks (\`\`\`) to format console output, logs, and code as it's very hard to read otherwise.
 1. **Output of checks**
-    * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (`sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
+    * Results of GitLab [Application Check](doc/install/installation.md#check-application-status) (For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:check SANITIZE=true`); For installations from source: `sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production SANITIZE=true`); we will only investigate if the tests are passing
     * Version of GitLab you are running; we will only investigate issues in the latest stable and development releases as per the [maintenance policy](MAINTENANCE.md)
     * Add the last commit SHA-1 of the GitLab version you used to replicate the issue (obtainable from the help page)
-    * Describe your setup (use relevant parts from `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
+    * Describe your setup (use relevant parts from the env info: For installations with omnibus-gitlab package: `sudo gitlab-rake gitlab:env:info`; For installations from source: `sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production`)
 1. **Possible fixes**: If you can, link to the line of code that might be responsible for the problem
 
 ## Merge requests
@@ -59,7 +69,7 @@ We welcome merge requests with fixes and improvements to GitLab code, tests, and
 
 Merge requests can be filed either at [gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/merge_requests) or [github.com](https://github.com/gitlabhq/gitlabhq/pulls).
 
-If you are new to GitLab development (or web development in general), search for the label `easyfix` ([gitlab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [github](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
+If you are new to GitLab development (or web development in general), search for the label `easyfix` ([GitLab.com](https://gitlab.com/gitlab-org/gitlab-ce/issues?label_name=easyfix), [GitHub](https://github.com/gitlabhq/gitlabhq/labels/easyfix)). Those are issues easy to fix, marked by the GitLab core-team. If you are unsure how to proceed but want to help, mention one of the core-team members to give you a hint.
 
 To start with GitLab download the [GitLab Development Kit](https://gitlab.com/gitlab-org/gitlab-development-kit) and see [Development section](doc/development/README.md) in the help file.
 
@@ -72,7 +82,7 @@ If you can, please submit a merge request with the fix or improvements including
 1. Write [tests](https://gitlab.com/gitlab-org/gitlab-development-kit#running-the-tests) and code
 1. Add your changes to the [CHANGELOG](CHANGELOG)
 1. If you are changing the README, some documentation or other things which have no effect on the tests, add `[ci skip]` somewhere in the commit message
-1. If you have multiple commits please combine them into one commit by [squashing them](http://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
+1. If you have multiple commits please combine them into one commit by [squashing them](https://git-scm.com/book/en/Git-Tools-Rewriting-History#Squashing-Commits)
 1. Push the commit to your fork
 1. Submit a merge request (MR) to the master branch
 1. The MR title should describe the change you want to make
@@ -99,7 +109,7 @@ If you contribute to GitLab please know that changes involve more than just code
 We have the following [definition of done](http://guide.agilealliance.org/guide/definition-of-done.html).
 Please ensure you support the feature you contribute through all of these steps.
 
-1. Description explaning the relevancy (see following item)
+1. Description explaining the relevancy (see following item)
 1. Working and clean code that is commented where needed
 1. Unit and integration tests that pass on the CI server
 1. Documented in the /doc directory
@@ -163,7 +173,7 @@ If you add a dependency in GitLab (such as an operating system package) please c
 1.  [Markdown](http://www.cirosantilli.com/markdown-styleguide)
 1.  [Database Migrations](doc/development/migration_style_guide.md)
 1.  [Documentation styleguide](doc_styleguide.md)
-1.  Interface text should be written subjectively instead of objectively. It should be the gitlab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
+1.  Interface text should be written subjectively instead of objectively. It should be the GitLab core team addressing a person. It should be written in present time and never use past tense (has been/was). For example instead of "prohibited this user from being saved due to the following errors:" the text should be "sorry, we could not create your account because:". Also these [excellent writing guidelines](https://github.com/NARKOZ/guides#writing).
 
 This is also the style used by linting tools such as [RuboCop](https://github.com/bbatsov/rubocop), [PullReview](https://www.pullreview.com/) and [Hound CI](https://houndci.com).
 
@@ -181,4 +191,4 @@ This code of conduct applies both within project spaces and in public spaces whe
 
 Instances of abusive, harassing, or otherwise unacceptable behavior can be reported by emailing contact@gitlab.com
 
-This Code of Conduct is adapted from the [Contributor Covenant](http:contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
+This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.1.0, available at [http://contributor-covenant.org/version/1/1/0/](http://contributor-covenant.org/version/1/1/0/)
diff --git a/GITLAB_SHELL_VERSION b/GITLAB_SHELL_VERSION
index 338a5b5d8fec491b97978114dc35e36348fa56a7..743af5e1251c4fc79e10f34ef7c9f24b1367ab72 100644
--- a/GITLAB_SHELL_VERSION
+++ b/GITLAB_SHELL_VERSION
@@ -1 +1 @@
-2.6.6
+2.6.8
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 9e11b32fcaa96816319e5d0dcff9fb2873f04061..2b7c5ae01848a77d95e2792eb83ab605c9aed91a 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.3.1
+0.4.2
diff --git a/Gemfile b/Gemfile
index 8a19885bcb199b49e5cfb081e1b43476d924e50a..67640bb9ae08ad8afed8d6b44f3244b5e75c4862 100644
--- a/Gemfile
+++ b/Gemfile
@@ -1,6 +1,10 @@
 source "https://rubygems.org"
 
-gem 'rails', '4.1.12'
+gem 'rails', '4.2.4'
+gem 'rails-deprecated_sanitizer', '~> 1.0.3'
+
+# Responders respond_to and respond_with
+gem 'responders', '~> 2.0'
 
 # Specify a sprockets version due to security issue
 # See https://groups.google.com/forum/#!topic/rubyonrails-security/doAVp0YaTqY
@@ -16,7 +20,7 @@ gem "pg", '~> 0.18.2', group: :postgres
 # Authentication libraries
 gem 'devise',                 '~> 3.5.2'
 gem 'devise-async',           '~> 0.9.0'
-gem 'doorkeeper',             '~> 2.1.3'
+gem 'doorkeeper',             '~> 2.2.0'
 gem 'omniauth',               '~> 1.2.2'
 gem 'omniauth-bitbucket',     '~> 0.0.2'
 gem 'omniauth-facebook',      '~> 3.0.0'
@@ -28,7 +32,7 @@ gem 'omniauth-saml',          '~> 1.4.0'
 gem 'omniauth-shibboleth',    '~> 1.2.0'
 gem 'omniauth-twitter',       '~> 1.2.0'
 gem 'omniauth_crowd'
-gem 'rack-oauth2',            '~> 1.0.5'
+gem 'rack-oauth2',            '~> 1.2.1'
 
 # Two-factor authentication
 gem 'devise-two-factor', '~> 2.0.0'
@@ -62,9 +66,6 @@ gem 'rack-cors',    '~> 0.4.0', require: 'rack/cors'
 # based on human-friendly examples
 gem "stamp", '~> 0.6.0'
 
-# Enumeration fields
-gem 'enumerize', '~> 0.7.0'
-
 # Pagination
 gem "kaminari", "~> 0.16.3"
 
@@ -95,9 +96,10 @@ gem 'redcarpet',     '~> 3.3.3'
 gem 'RedCloth',      '~> 4.2.9'
 gem 'rdoc',          '~>3.6'
 gem 'org-ruby',      '~> 0.9.12'
-gem 'creole',        '~>0.3.6'
+gem 'creole',        '~> 0.5.0'
 gem 'wikicloth',     '0.8.1'
 gem 'asciidoctor',   '~> 1.5.2'
+gem 'net-ssh',       '~> 3.0.1'
 
 # Diffs
 gem 'diffy', '~> 3.0.3'
@@ -125,8 +127,7 @@ gem 'sidetiq', '~> 0.6.3'
 gem "httparty", '~> 0.13.3'
 
 # Colored output to console
-gem "colored", '~> 1.2'
-gem "colorize", '~> 0.5.8'
+gem "colorize", '~> 0.7.0'
 
 # GitLab settings
 gem 'settingslogic', '~> 2.0.9'
@@ -154,7 +155,7 @@ gem "gemnasium-gitlab-service", "~> 0.2"
 gem "slack-notifier", "~> 1.2.0"
 
 # Asana integration
-gem 'asana', '~> 0.0.6'
+gem 'asana', '~> 0.4.0'
 
 # FogBugz integration
 gem 'ruby-fogbugz', '~> 0.2.1'
@@ -187,13 +188,13 @@ gem "sass-rails", '~> 4.0.5'
 gem "coffee-rails", '~> 4.1.0'
 gem "uglifier", '~> 2.7.2'
 gem 'turbolinks', '~> 2.5.0'
-gem 'jquery-turbolinks', '~> 2.0.1'
+gem 'jquery-turbolinks', '~> 2.1.0'
 
 gem 'addressable',        '~> 2.3.8'
 gem 'bootstrap-sass',     '~> 3.0'
 gem 'font-awesome-rails', '~> 4.2'
 gem 'gitlab_emoji',       '~> 0.1'
-gem 'gon',                '~> 5.0.0'
+gem 'gon',                '~> 6.0.1'
 gem 'jquery-atwho-rails', '~> 1.3.2'
 gem 'jquery-rails',       '~> 3.1.3'
 gem 'jquery-scrollto-rails', '~> 1.4.3'
@@ -214,6 +215,7 @@ group :development do
   gem 'rerun', '~> 0.10.0'
   gem 'bullet', require: false
   gem 'rblineprof', platform: :mri, require: false
+  gem 'web-console', '~> 2.0'
 
   # Better errors handler
   gem 'better_errors', '~> 1.0.1'
@@ -261,6 +263,7 @@ group :development, :test do
   gem 'simplecov', '~> 0.10.0', require: false
   gem 'flog', require: false
   gem 'flay', require: false
+  gem 'bundler-audit', require: false
 
   gem 'benchmark-ips', require: false
 end
@@ -269,7 +272,7 @@ group :test do
   gem 'shoulda-matchers', '~> 2.8.0', require: false
   gem 'email_spec', '~> 1.6.0'
   gem 'webmock', '~> 1.21.0'
-  gem 'test_after_commit', '~> 0.2.2'
+  gem 'test_after_commit', '~> 0.4.2'
   gem 'sham_rack'
 end
 
diff --git a/Gemfile.lock b/Gemfile.lock
index 99cdc2a50aee35acf5dbc4b2dbc35bf62cc220a1..cd1855758b2083d135a4f7932efe20443a42e70f 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -1,63 +1,71 @@
 GEM
   remote: https://rubygems.org/
   specs:
-    CFPropertyList (2.3.1)
+    CFPropertyList (2.3.2)
     RedCloth (4.2.9)
     ace-rails-ap (2.0.1)
-    actionmailer (4.1.12)
-      actionpack (= 4.1.12)
-      actionview (= 4.1.12)
+    actionmailer (4.2.4)
+      actionpack (= 4.2.4)
+      actionview (= 4.2.4)
+      activejob (= 4.2.4)
       mail (~> 2.5, >= 2.5.4)
-    actionpack (4.1.12)
-      actionview (= 4.1.12)
-      activesupport (= 4.1.12)
-      rack (~> 1.5.2)
+      rails-dom-testing (~> 1.0, >= 1.0.5)
+    actionpack (4.2.4)
+      actionview (= 4.2.4)
+      activesupport (= 4.2.4)
+      rack (~> 1.6)
       rack-test (~> 0.6.2)
-    actionview (4.1.12)
-      activesupport (= 4.1.12)
+      rails-dom-testing (~> 1.0, >= 1.0.5)
+      rails-html-sanitizer (~> 1.0, >= 1.0.2)
+    actionview (4.2.4)
+      activesupport (= 4.2.4)
       builder (~> 3.1)
       erubis (~> 2.7.0)
-    activemodel (4.1.12)
-      activesupport (= 4.1.12)
+      rails-dom-testing (~> 1.0, >= 1.0.5)
+      rails-html-sanitizer (~> 1.0, >= 1.0.2)
+    activejob (4.2.4)
+      activesupport (= 4.2.4)
+      globalid (>= 0.3.0)
+    activemodel (4.2.4)
+      activesupport (= 4.2.4)
       builder (~> 3.1)
-    activerecord (4.1.12)
-      activemodel (= 4.1.12)
-      activesupport (= 4.1.12)
-      arel (~> 5.0.0)
+    activerecord (4.2.4)
+      activemodel (= 4.2.4)
+      activesupport (= 4.2.4)
+      arel (~> 6.0)
     activerecord-deprecated_finders (1.0.4)
-    activerecord-session_store (0.1.1)
+    activerecord-session_store (0.1.2)
       actionpack (>= 4.0.0, < 5)
       activerecord (>= 4.0.0, < 5)
       railties (>= 4.0.0, < 5)
-    activeresource (4.0.0)
-      activemodel (~> 4.0)
-      activesupport (~> 4.0)
-      rails-observers (~> 0.1.1)
-    activesupport (4.1.12)
-      i18n (~> 0.6, >= 0.6.9)
+    activesupport (4.2.4)
+      i18n (~> 0.7)
       json (~> 1.7, >= 1.7.7)
       minitest (~> 5.1)
-      thread_safe (~> 0.1)
+      thread_safe (~> 0.3, >= 0.3.4)
       tzinfo (~> 1.1)
     acts-as-taggable-on (3.5.0)
       activerecord (>= 3.2, < 5)
     addressable (2.3.8)
-    after_commit_queue (1.1.0)
-      rails (>= 3.0)
+    after_commit_queue (1.3.0)
+      activerecord (>= 3.0)
     annotate (2.6.10)
       activerecord (>= 3.2, <= 4.3)
       rake (~> 10.4)
-    arel (5.0.1.20140414130214)
-    asana (0.0.6)
-      activeresource (>= 3.2.3)
-    asciidoctor (1.5.2)
+    arel (6.0.3)
+    asana (0.4.0)
+      faraday (~> 0.9)
+      faraday_middleware (~> 0.9)
+      faraday_middleware-multi_json (~> 0.0)
+      oauth2 (~> 1.0)
+    asciidoctor (1.5.3)
     ast (2.1.0)
     astrolabe (1.3.1)
       parser (~> 2.2)
     attr_encrypted (1.3.4)
       encryptor (>= 1.3.0)
     attr_required (1.0.0)
-    autoprefixer-rails (5.2.1.2)
+    autoprefixer-rails (6.1.1)
       execjs
       json
     awesome_print (1.2.0)
@@ -85,12 +93,15 @@ GEM
       ruby_parser (~> 3.5.0)
       sass (~> 3.0)
       terminal-table (~> 1.4)
-    browser (1.0.0)
+    browser (1.0.1)
     builder (3.2.2)
-    bullet (4.14.9)
+    bullet (4.14.10)
       activesupport (>= 3.0.0)
       uniform_notifier (~> 1.9.0)
-    byebug (6.0.2)
+    bundler-audit (0.4.0)
+      bundler (~> 1.2)
+      thor (~> 0.18)
+    byebug (8.2.0)
     cal-heatmap-rails (0.0.1)
     capybara (2.4.4)
       mime-types (>= 1.16)
@@ -108,7 +119,7 @@ GEM
     celluloid (0.16.0)
       timers (~> 4.0.0)
     charlock_holmes (0.7.3)
-    chunky_png (1.3.4)
+    chunky_png (1.3.5)
     cliver (0.3.2)
     coderay (1.1.0)
     coercible (1.0.0)
@@ -119,19 +130,19 @@ GEM
     coffee-script (2.4.1)
       coffee-script-source
       execjs
-    coffee-script-source (1.9.1.1)
-    colored (1.2)
-    colorize (0.5.8)
+    coffee-script-source (1.10.0)
+    colorize (0.7.7)
     connection_pool (2.2.0)
-    coveralls (0.8.2)
+    coveralls (0.8.9)
       json (~> 1.8)
       rest-client (>= 1.6.8, < 2)
       simplecov (~> 0.10.0)
       term-ansicolor (~> 1.3)
       thor (~> 0.19.1)
+      tins (~> 1.6.0)
     crack (0.4.2)
       safe_yaml (~> 1.0.0)
-    creole (0.3.8)
+    creole (0.5.0)
     d3_rails (3.5.6)
       railties (>= 3.1.0)
     daemons (1.2.3)
@@ -151,7 +162,7 @@ GEM
       warden (~> 1.2.3)
     devise-async (0.9.0)
       devise (~> 3.2)
-    devise-two-factor (2.0.0)
+    devise-two-factor (2.0.1)
       activesupport
       attr_encrypted (~> 1.3.2)
       devise (~> 3.5.0)
@@ -160,19 +171,17 @@ GEM
     diff-lcs (1.2.5)
     diffy (3.0.7)
     docile (1.1.5)
-    domain_name (0.5.24)
+    domain_name (0.5.25)
       unf (>= 0.0.5, < 1.0.0)
-    doorkeeper (2.1.4)
+    doorkeeper (2.2.2)
       railties (>= 3.2)
-    dropzonejs-rails (0.7.1)
+    dropzonejs-rails (0.7.2)
       rails (> 3.1)
     email_reply_parser (0.5.8)
     email_spec (1.6.0)
       launchy (~> 2.1)
       mail (~> 2.2)
     encryptor (1.3.0)
-    enumerize (0.7.0)
-      activesupport (>= 3.2)
     equalizer (0.0.11)
     erubis (2.7.0)
     escape_utils (1.1.0)
@@ -189,6 +198,9 @@ GEM
       multipart-post (>= 1.2, < 3)
     faraday_middleware (0.10.0)
       faraday (>= 0.7.4, < 0.10)
+    faraday_middleware-multi_json (0.0.6)
+      faraday_middleware
+      multi_json
     fastercsv (1.5.5)
     ffaker (2.0.0)
     ffi (1.9.10)
@@ -200,7 +212,7 @@ GEM
     flog (4.3.2)
       ruby_parser (~> 3.1, > 3.1.0)
       sexp_processor (~> 4.4)
-    flowdock (0.7.0)
+    flowdock (0.7.1)
       httparty (~> 0.7)
       multi_json
     fog (1.25.0)
@@ -222,13 +234,10 @@ GEM
       fog-core (~> 1.22)
       fog-json
       inflecto (~> 0.0.2)
-    fog-core (1.32.1)
+    fog-core (1.35.0)
       builder
       excon (~> 0.45)
       formatador (~> 0.2)
-      mime-types
-      net-scp (~> 1.1)
-      net-ssh (>= 2.1.3)
     fog-json (1.0.2)
       fog-core (~> 1.0)
       multi_json (~> 1.10)
@@ -240,10 +249,10 @@ GEM
       fog-core (>= 1.21.0)
       fog-json
       fog-xml (>= 0.0.1)
-    fog-sakuracloud (1.0.1)
+    fog-sakuracloud (1.4.0)
       fog-core
       fog-json
-    fog-softlayer (0.4.7)
+    fog-softlayer (1.0.2)
       fog-core
       fog-json
     fog-terremark (0.1.0)
@@ -258,7 +267,7 @@ GEM
     fog-xml (0.1.2)
       fog-core
       nokogiri (~> 1.5, >= 1.5.11)
-    font-awesome-rails (4.4.0.0)
+    font-awesome-rails (4.5.0.0)
       railties (>= 3.2, < 5.0)
     foreman (0.78.0)
       thor (~> 0.19.1)
@@ -268,11 +277,11 @@ GEM
       ruby-progressbar (~> 1.4)
     gemnasium-gitlab-service (0.2.6)
       rugged (~> 0.21)
-    gemojione (2.0.1)
+    gemojione (2.1.0)
       json
     get_process_mem (0.2.0)
     gherkin-ruby (0.3.2)
-    github-linguist (4.7.0)
+    github-linguist (4.7.2)
       charlock_holmes (~> 0.7.3)
       escape_utils (~> 1.1.0)
       mime-types (>= 1.19)
@@ -287,8 +296,8 @@ GEM
       diff-lcs (~> 1.1)
       mime-types (~> 1.15)
       posix-spawn (~> 0.3)
-    gitlab_emoji (0.1.1)
-      gemojione (~> 2.0)
+    gitlab_emoji (0.2.0)
+      gemojione (~> 2.1)
     gitlab_git (7.2.20)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.7.3)
@@ -300,6 +309,8 @@ GEM
       omniauth (~> 1.0)
       pyu-ruby-sasl (~> 0.0.3.1)
       rubyntlm (~> 0.3)
+    globalid (0.3.6)
+      activesupport (>= 4.1.0)
     gollum-grit_adapter (1.0.0)
       gitlab-grit (~> 2.7, >= 2.7.1)
     gollum-lib (4.0.3)
@@ -309,9 +320,11 @@ GEM
       rouge (~> 1.10.1)
       sanitize (~> 2.1.0)
       stringex (~> 2.5.1)
-    gon (5.0.4)
-      actionpack (>= 2.3.0)
+    gon (6.0.1)
+      actionpack (>= 3.0)
       json
+      multi_json
+      request_store (>= 1.0)
     grape (0.13.0)
       activesupport
       builder
@@ -333,7 +346,7 @@ GEM
       haml (>= 4.0.6, < 5.0)
       html2haml (>= 1.0.1)
       railties (>= 4.0.1)
-    hashie (3.4.2)
+    hashie (3.4.3)
     highline (1.6.21)
     hike (1.2.3)
     hipchat (1.5.2)
@@ -351,40 +364,42 @@ GEM
     http-cookie (1.0.2)
       domain_name (~> 0.5)
     http_parser.rb (0.5.3)
-    httparty (0.13.5)
+    httparty (0.13.7)
       json (~> 1.8)
       multi_xml (>= 0.5.2)
-    httpclient (2.6.0.1)
+    httpclient (2.7.0.1)
     i18n (0.7.0)
     ice_cube (0.11.1)
     ice_nine (0.11.1)
     inflecto (0.0.2)
     ipaddress (0.8.0)
     jquery-atwho-rails (1.3.2)
-    jquery-rails (3.1.3)
+    jquery-rails (3.1.4)
       railties (>= 3.0, < 5.0)
       thor (>= 0.14, < 2.0)
     jquery-scrollto-rails (1.4.3)
       railties (> 3.1, < 5.0)
-    jquery-turbolinks (2.0.2)
+    jquery-turbolinks (2.1.0)
       railties (>= 3.1.0)
       turbolinks
     jquery-ui-rails (4.2.1)
       railties (>= 3.2.16)
     json (1.8.3)
-    jwt (1.5.1)
+    jwt (1.5.2)
     kaminari (0.16.3)
       actionpack (>= 3.0.0)
       activesupport (>= 3.0.0)
-    kgio (2.9.3)
+    kgio (2.10.0)
     launchy (2.4.3)
       addressable (~> 2.3)
     letter_opener (1.1.2)
       launchy (~> 2.2)
-    listen (2.10.1)
-      celluloid (~> 0.16.0)
+    listen (2.9.0)
+      celluloid (>= 0.15.2)
       rb-fsevent (>= 0.9.3)
       rb-inotify (>= 0.9)
+    loofah (2.0.3)
+      nokogiri (>= 1.5.9)
     macaddr (1.7.1)
       systemu (~> 2.6.2)
     mail (2.6.3)
@@ -401,16 +416,14 @@ GEM
     multipart-post (2.0.0)
     mysql2 (0.3.20)
     nested_form (0.3.2)
-    net-ldap (0.11)
-    net-scp (1.2.1)
-      net-ssh (>= 2.6.5)
-    net-ssh (2.9.2)
-    netrc (0.10.3)
+    net-ldap (0.12.1)
+    net-ssh (3.0.1)
+    netrc (0.11.0)
     newrelic-grape (2.0.0)
       grape
       newrelic_rpm
     newrelic_rpm (3.9.4.245)
-    nokogiri (1.6.6.2)
+    nokogiri (1.6.6.4)
       mini_portile (~> 0.6.0)
     nprogress-rails (0.1.6.7)
     oauth (0.4.7)
@@ -434,12 +447,15 @@ GEM
     omniauth-github (1.1.2)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.1)
-    omniauth-gitlab (1.0.0)
+    omniauth-gitlab (1.0.1)
       omniauth (~> 1.0)
       omniauth-oauth2 (~> 1.0)
-    omniauth-google-oauth2 (0.2.6)
-      omniauth (> 1.0)
-      omniauth-oauth2 (~> 1.1)
+    omniauth-google-oauth2 (0.2.10)
+      addressable (~> 2.3)
+      jwt (~> 1.0)
+      multi_json (~> 1.3)
+      omniauth (>= 1.1.1)
+      omniauth-oauth2 (~> 1.3.1)
     omniauth-kerberos (0.3.0)
       omniauth-multipassword
       timfel-krb5-auth (~> 0.8)
@@ -463,18 +479,18 @@ GEM
       activesupport
       nokogiri (>= 1.4.4)
       omniauth (~> 1.0)
-    opennebula (4.12.1)
+    opennebula (4.14.2)
       json
       nokogiri
       rbvmomi
     org-ruby (0.9.12)
       rubypants (~> 0.2)
     orm_adapter (0.5.0)
-    paranoia (2.1.3)
+    paranoia (2.1.4)
       activerecord (~> 4.0)
-    parser (2.2.2.6)
+    parser (2.2.3.0)
       ast (>= 1.1, < 3.0)
-    pg (0.18.2)
+    pg (0.18.4)
     poltergeist (1.6.0)
       capybara (~> 2.1)
       cliver (~> 0.3.1)
@@ -482,7 +498,7 @@ GEM
       websocket-driver (>= 0.2.0)
     posix-spawn (0.3.11)
     powerpack (0.0.9)
-    pry (0.10.1)
+    pry (0.10.3)
       coderay (~> 1.1.0)
       method_source (~> 0.8.1)
       slop (~> 3.4)
@@ -491,7 +507,7 @@ GEM
     pyu-ruby-sasl (0.0.3.3)
     quiet_assets (1.0.3)
       railties (>= 3.1, < 5.0)
-    rack (1.5.5)
+    rack (1.6.4)
     rack-accept (0.4.5)
       rack (>= 0.4)
     rack-attack (4.3.0)
@@ -499,7 +515,7 @@ GEM
     rack-cors (0.4.0)
     rack-mount (0.8.3)
       rack (>= 1.0.0)
-    rack-oauth2 (1.0.10)
+    rack-oauth2 (1.2.1)
       activesupport (>= 2.3)
       attr_required (>= 0.0.5)
       httpclient (>= 2.4)
@@ -509,28 +525,35 @@ GEM
       rack
     rack-test (0.6.3)
       rack (>= 1.0)
-    rails (4.1.12)
-      actionmailer (= 4.1.12)
-      actionpack (= 4.1.12)
-      actionview (= 4.1.12)
-      activemodel (= 4.1.12)
-      activerecord (= 4.1.12)
-      activesupport (= 4.1.12)
+    rails (4.2.4)
+      actionmailer (= 4.2.4)
+      actionpack (= 4.2.4)
+      actionview (= 4.2.4)
+      activejob (= 4.2.4)
+      activemodel (= 4.2.4)
+      activerecord (= 4.2.4)
+      activesupport (= 4.2.4)
       bundler (>= 1.3.0, < 2.0)
-      railties (= 4.1.12)
-      sprockets-rails (~> 2.0)
-    rails-observers (0.1.2)
-      activemodel (~> 4.0)
-    railties (4.1.12)
-      actionpack (= 4.1.12)
-      activesupport (= 4.1.12)
+      railties (= 4.2.4)
+      sprockets-rails
+    rails-deprecated_sanitizer (1.0.3)
+      activesupport (>= 4.2.0.alpha)
+    rails-dom-testing (1.0.7)
+      activesupport (>= 4.2.0.beta, < 5.0)
+      nokogiri (~> 1.6.0)
+      rails-deprecated_sanitizer (>= 1.0.1)
+    rails-html-sanitizer (1.0.2)
+      loofah (~> 2.0)
+    railties (4.2.4)
+      actionpack (= 4.2.4)
+      activesupport (= 4.2.4)
       rake (>= 0.8.7)
       thor (>= 0.18.1, < 2.0)
     rainbow (2.0.0)
     raindrops (0.15.0)
     rake (10.4.2)
     raphael-rails (2.1.2)
-    rb-fsevent (0.9.5)
+    rb-fsevent (0.9.6)
     rb-inotify (0.9.5)
       ffi (>= 0.5.0)
     rblineprof (0.3.6)
@@ -542,13 +565,13 @@ GEM
     rdoc (3.12.2)
       json (~> 1.4)
     redcarpet (3.3.3)
-    redis (3.2.1)
-    redis-actionpack (4.0.0)
+    redis (3.2.2)
+    redis-actionpack (4.0.1)
       actionpack (~> 4)
       redis-rack (~> 1.5.0)
       redis-store (~> 1.1.0)
-    redis-activesupport (4.1.1)
-      activesupport (~> 4)
+    redis-activesupport (4.1.5)
+      activesupport (>= 3, < 5)
       redis-store (~> 1.1.0)
     redis-namespace (1.5.2)
       redis (~> 3.0, >= 3.0.4)
@@ -559,13 +582,13 @@ GEM
       redis-actionpack (~> 4)
       redis-activesupport (~> 4)
       redis-store (~> 1.1.0)
-    redis-store (1.1.6)
+    redis-store (1.1.7)
       redis (>= 2.2)
-    request_store (1.2.0)
+    request_store (1.2.1)
     rerun (0.10.0)
       listen (~> 2.7, >= 2.7.3)
-    responders (1.1.2)
-      railties (>= 3.2, < 4.2)
+    responders (2.1.0)
+      railties (>= 4.2.0, < 5)
     rest-client (1.8.0)
       http-cookie (>= 1.0.2, < 2.0)
       mime-types (>= 1.16, < 3.0)
@@ -687,7 +710,7 @@ GEM
       multi_json (~> 1.0)
       rack (~> 1.0)
       tilt (~> 1.1, != 1.3.0)
-    sprockets-rails (2.3.2)
+    sprockets-rails (2.3.3)
       actionpack (>= 3.0)
       activesupport (>= 3.0)
       sprockets (>= 2.8, < 4.0)
@@ -710,11 +733,11 @@ GEM
     term-ansicolor (1.3.2)
       tins (~> 1.0)
     terminal-table (1.5.2)
-    test_after_commit (0.2.7)
+    test_after_commit (0.4.2)
       activerecord (>= 3.2)
-    thin (1.6.3)
+    thin (1.6.4)
       daemons (~> 1.0, >= 1.0.9)
-      eventmachine (~> 1.0)
+      eventmachine (~> 1.0, >= 1.0.4)
       rack (~> 1.0)
     thor (0.19.1)
     thread_safe (0.3.5)
@@ -752,9 +775,9 @@ GEM
       kgio (~> 2.6)
       rack
       raindrops (~> 0.7)
-    unicorn-worker-killer (0.4.3)
+    unicorn-worker-killer (0.4.4)
       get_process_mem (~> 0)
-      unicorn (~> 4)
+      unicorn (>= 4, < 6)
     uniform_notifier (1.9.0)
     uuid (2.3.8)
       macaddr (~> 1.0)
@@ -766,10 +789,15 @@ GEM
       equalizer (~> 0.0, >= 0.0.9)
     warden (1.2.3)
       rack (>= 1.0)
+    web-console (2.2.1)
+      activemodel (>= 4.0)
+      binding_of_caller (>= 0.7.2)
+      railties (>= 4.0)
+      sprockets-rails (>= 2.0, < 4.0)
     webmock (1.21.0)
       addressable (>= 2.3.6)
       crack (>= 0.3.2)
-    websocket-driver (0.6.2)
+    websocket-driver (0.6.3)
       websocket-extensions (>= 0.1.0)
     websocket-extensions (0.1.2)
     wikicloth (0.8.1)
@@ -791,7 +819,7 @@ DEPENDENCIES
   addressable (~> 2.3.8)
   after_commit_queue
   annotate (~> 2.6.0)
-  asana (~> 0.0.6)
+  asana (~> 0.4.0)
   asciidoctor (~> 1.5.2)
   attr_encrypted (~> 1.3.4)
   awesome_print (~> 1.2.0)
@@ -802,6 +830,7 @@ DEPENDENCIES
   brakeman (= 3.0.1)
   browser (~> 1.0.0)
   bullet
+  bundler-audit
   byebug
   cal-heatmap-rails (~> 0.0.1)
   capybara (~> 2.4.0)
@@ -809,10 +838,9 @@ DEPENDENCIES
   carrierwave (~> 0.9.0)
   charlock_holmes (~> 0.7.3)
   coffee-rails (~> 4.1.0)
-  colored (~> 1.2)
-  colorize (~> 0.5.8)
+  colorize (~> 0.7.0)
   coveralls (~> 0.8.2)
-  creole (~> 0.3.6)
+  creole (~> 0.5.0)
   d3_rails (~> 3.5.5)
   database_cleaner (~> 1.4.0)
   default_value_for (~> 3.0.0)
@@ -820,11 +848,10 @@ DEPENDENCIES
   devise-async (~> 0.9.0)
   devise-two-factor (~> 2.0.0)
   diffy (~> 3.0.3)
-  doorkeeper (~> 2.1.3)
+  doorkeeper (~> 2.2.0)
   dropzonejs-rails (~> 0.7.1)
   email_reply_parser (~> 0.5.8)
   email_spec (~> 1.6.0)
-  enumerize (~> 0.7.0)
   factory_girl_rails (~> 4.3.0)
   ffaker (~> 2.0.0)
   flay
@@ -842,7 +869,7 @@ DEPENDENCIES
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (~> 1.2.1)
   gollum-lib (~> 4.0.2)
-  gon (~> 5.0.0)
+  gon (~> 6.0.1)
   grape (~> 0.13.0)
   grape-entity (~> 0.4.2)
   haml-rails (~> 0.9.0)
@@ -852,7 +879,7 @@ DEPENDENCIES
   jquery-atwho-rails (~> 1.3.2)
   jquery-rails (~> 3.1.3)
   jquery-scrollto-rails (~> 1.4.3)
-  jquery-turbolinks (~> 2.0.1)
+  jquery-turbolinks (~> 2.1.0)
   jquery-ui-rails (~> 4.2.1)
   kaminari (~> 0.16.3)
   letter_opener (~> 1.1.2)
@@ -861,6 +888,7 @@ DEPENDENCIES
   mousetrap-rails (~> 1.4.6)
   mysql2 (~> 0.3.16)
   nested_form (~> 0.3.2)
+  net-ssh (~> 3.0.1)
   newrelic-grape
   newrelic_rpm (~> 3.9.4.245)
   nprogress-rails (~> 0.1.6.7)
@@ -885,8 +913,9 @@ DEPENDENCIES
   quiet_assets (~> 1.0.2)
   rack-attack (~> 4.3.0)
   rack-cors (~> 0.4.0)
-  rack-oauth2 (~> 1.0.5)
-  rails (= 4.1.12)
+  rack-oauth2 (~> 1.2.1)
+  rails (= 4.2.4)
+  rails-deprecated_sanitizer (~> 1.0.3)
   raphael-rails (~> 2.1.2)
   rblineprof
   rdoc (~> 3.6)
@@ -894,6 +923,7 @@ DEPENDENCIES
   redis-rails (~> 4.0.0)
   request_store (~> 1.2.0)
   rerun (~> 0.10.0)
+  responders (~> 2.0)
   rqrcode-rails3 (~> 0.1.7)
   rspec-rails (~> 3.3.0)
   rubocop (~> 0.28.0)
@@ -923,7 +953,7 @@ DEPENDENCIES
   task_list (~> 1.0.2)
   teaspoon (~> 1.0.0)
   teaspoon-jasmine (~> 2.2.0)
-  test_after_commit (~> 0.2.2)
+  test_after_commit (~> 0.4.2)
   thin (~> 1.6.1)
   tinder (~> 1.10.0)
   turbolinks (~> 2.5.0)
@@ -934,6 +964,7 @@ DEPENDENCIES
   unicorn-worker-killer (~> 0.4.2)
   version_sorter (~> 2.0.0)
   virtus (~> 1.0.1)
+  web-console (~> 2.0)
   webmock (~> 1.21.0)
   wikicloth (= 0.8.1)
 
diff --git a/PROCESS.md b/PROCESS.md
index a4b0c83644b9a8c1d2a7d2313ea38c72ac5f1f77..72fc3481447caec2189bb2c8316c8f54695cbbcf 100644
--- a/PROCESS.md
+++ b/PROCESS.md
@@ -34,13 +34,19 @@ The most important thing is making sure valid issues receive feedback from the d
 
 ## Workflow labels
 
-Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue. 
+Workflow labels are purposely not very detailed since that would be hard to keep updated as you would need to re-evaluate them after every comment. We optionally use functional labels on demand when want to group related issues to get an overview (for example all issues related to RVM, to tackle them in one go) and to add details to the issue.
 
 -   *Awaiting feedback*: Feedback pending from the reporter
 -   *Awaiting confirmation of fix*: The issue should already be solved in **master** (generally you can avoid this workflow item and just close the issue right away)
 -   *Attached MR*: There is a MR attached and the discussion should happen there
     - We need to let issues stay in sync with the MR's. We can do this with a "Closing #XXXX" or "Fixes #XXXX" comment in the MR. We can't close the issue when there is a merge request because sometimes a MR is not good and we just close the MR, then the issue must stay.
--   *Awaiting developer action/feedback*: Issue needs to be fixed or clarified by a developer
+- *Developer*: needs help from a developer
+- *UX* needs needs help from a UX designer
+- *Frontend* needs help from a Front-end engineer
+- *Graphics* needs help from a Graphics designer
+- *up-for-grabs* is an issue suitable for first-time contributors, of reasonable difficulty and size. Not exclusive with other labels.
+
+Example workflow: when a UX designer provided a design but it needs frontend work they remove the UX label and add the frontend label.
 
 ## Functional labels
 
diff --git a/Procfile b/Procfile
index 08880b9c425aa2af24dc07bd904b72bfd4084b4c..2e41485677ced70e425d4b2073250c3153b01e78 100644
--- a/Procfile
+++ b/Procfile
@@ -1,3 +1,7 @@
+# For DEVELOPMENT only. Production uses Runit in
+# https://gitlab.com/gitlab-org/omnibus-gitlab or the init scripts in
+# lib/support/init.d, which call scripts in bin/ .
+#
 web: bundle exec unicorn_rails -p ${PORT:="3000"} -E ${RAILS_ENV:="development"} -c ${UNICORN_CONFIG:="config/unicorn.rb"}
-worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
+worker: bundle exec sidekiq -q post_receive -q mailer -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q mailers -q default
 # mail_room: bundle exec mail_room -q -c config/mail_room.yml
diff --git a/README.md b/README.md
index 52e2d9776208e95b7d5c11bcfc7f2fe63fcab34c..c59c8593eba16e7c818b5a6b56c316a224a7e286 100644
--- a/README.md
+++ b/README.md
@@ -27,8 +27,6 @@ There are two editions of GitLab:
 - GitLab Community Edition (CE) is available freely under the MIT Expat license.
 - GitLab Enterprise Edition (EE) includes [extra features](https://about.gitlab.com/features/#compare) that are more useful for organizations with more than 100 users. To use EE and get official support please [become a subscriber](https://about.gitlab.com/pricing/).
 
-Included with the GitLab Omnibus Packages is [GitLab CI](https://about.gitlab.com/gitlab-ci/) that can easily build, test and deploy code.
-
 ## Website
 
 On [about.gitlab.com](https://about.gitlab.com/) you can find more information about:
diff --git a/VERSION b/VERSION
index a2264f05f500e20edac8133bf7b800dc35bc37bd..8d0676ff07ba5372063bb36af9529a8d976c16ad 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.1.0.pre
+8.3.0.pre
diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png
index 60021d5ac47686cce70570d22bad6670082caf56..7d89da97e11a0030fbab56b3d172f547ac9f896e 100644
Binary files a/app/assets/images/icon-link.png and b/app/assets/images/icon-link.png differ
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..09b48fe5572ab09b4bc97430d3d6a6f5ce846866
--- /dev/null
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -0,0 +1,91 @@
+class @AwardsHandler
+  constructor: (@post_emoji_url, @noteable_type, @noteable_id) ->
+
+  addAward: (emoji) ->
+    @postEmoji emoji, =>
+      @addAwardToEmojiBar(emoji)
+    
+  addAwardToEmojiBar: (emoji, custom_path = '') ->
+    if @exist(emoji)
+      if @isActive(emoji)
+        @decrementCounter(emoji)
+      else
+        counter = @findEmojiIcon(emoji).siblings(".counter")
+        counter.text(parseInt(counter.text()) + 1)
+        counter.parent().addClass("active")
+        @addMeToAuthorList(emoji)
+    else
+      @createEmoji(emoji, custom_path)
+
+  exist: (emoji) ->
+    @findEmojiIcon(emoji).length > 0
+
+  isActive: (emoji) ->
+    @findEmojiIcon(emoji).parent().hasClass("active")
+
+  decrementCounter: (emoji) ->
+    counter = @findEmojiIcon(emoji).siblings(".counter")
+
+    if parseInt(counter.text()) > 1
+      counter.text(parseInt(counter.text()) - 1)
+      counter.parent().removeClass("active")
+      @removeMeFromAuthorList(emoji)
+    else
+      award = counter.parent()
+      award.tooltip("destroy")
+      award.remove()
+
+  removeMeFromAuthorList: (emoji) ->
+    award_block = @findEmojiIcon(emoji).parent()
+    authors = award_block.attr("data-original-title").split(", ")
+    authors = _.without(authors, "me").join(", ")
+    award_block.attr("title", authors)
+    @resetTooltip(award_block)
+
+  addMeToAuthorList: (emoji) ->
+    award_block = @findEmojiIcon(emoji).parent()
+    authors = award_block.attr("data-original-title").split(", ")
+    authors.push("me")
+    award_block.attr("title", authors.join(", "))
+    @resetTooltip(award_block)
+
+  resetTooltip: (award) ->
+    award.tooltip("destroy")
+
+    # "destroy" call is asynchronous, this is why we need to set timeout.
+    setTimeout (->
+      award.tooltip()
+    ), 200
+    
+
+  createEmoji: (emoji, custom_path) ->
+    nodes = []
+    nodes.push("<div class='award active' title='me'>")
+    nodes.push("<div class='icon' data-emoji='" + emoji + "'>")
+    nodes.push(@getImage(emoji, custom_path))
+    nodes.push("</div>")
+    nodes.push("<div class='counter'>1")
+    nodes.push("</div></div>")
+
+    $(".awards-controls").before(nodes.join("\n"))
+
+    $(".award").tooltip()
+
+  getImage: (emoji, custom_path) ->
+    if custom_path
+      $("<img>").attr({src: custom_path, width: 20, height: 20}).wrap("<div>").parent().html()
+    else
+      $("li[data-emoji='" + emoji + "']").html()
+
+
+  postEmoji: (emoji, callback) ->
+    $.post @post_emoji_url, { note: {
+      note: ":" + emoji + ":"
+      noteable_type: @noteable_type
+      noteable_id: @noteable_id
+    }},(data) ->
+      if data.ok
+        callback.call()
+
+  findEmojiIcon: (emoji) ->
+    $(".icon[data-emoji='" + emoji + "']")
\ No newline at end of file
diff --git a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
index 5b604adbbb1bca76236caaee1f5395f6cb7f4059..195f8b11e5dbac896379cd30d13f3caa3532e130 100644
--- a/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
+++ b/app/assets/javascripts/blob/blob_file_dropzone.js.coffee
@@ -23,18 +23,6 @@ class @BlobFileDropzone
       init: ->
         this.on 'addedfile', (file) ->
           $('.dropzone-alerts').html('').hide()
-          commit_message = form.find('#commit_message')[0]
-
-          if /^Upload/.test(commit_message.placeholder)
-            commit_message.placeholder = 'Upload ' + file.name
-
-          return
-
-        this.on 'removedfile', (file) ->
-          commit_message = form.find('#commit_message')[0]
-
-          if /^Upload/.test(commit_message.placeholder)
-            commit_message.placeholder = 'Upload new file'
 
           return
 
@@ -47,8 +35,9 @@ class @BlobFileDropzone
           return
 
         this.on 'sending', (file, xhr, formData) ->
-          formData.append('new_branch', form.find('#new_branch').val())
-          formData.append('commit_message', form.find('#commit_message').val())
+          formData.append('new_branch', form.find('.js-new-branch').val())
+          formData.append('create_merge_request', form.find('.js-create-merge-request').val())
+          formData.append('commit_message', form.find('.js-commit-message').val())
           return
 
       # Override behavior of adding error underneath preview
diff --git a/app/assets/javascripts/copy_to_clipboard.js.coffee b/app/assets/javascripts/copy_to_clipboard.js.coffee
index ec4b80cca6f51e5ac62e7061602e4c93d848a28b..24301e01b10de7394f68498b8f1c1627274acba3 100644
--- a/app/assets/javascripts/copy_to_clipboard.js.coffee
+++ b/app/assets/javascripts/copy_to_clipboard.js.coffee
@@ -1,21 +1,37 @@
 #= require clipboard
 
-$ ->
-  clipboard = new Clipboard '.js-clipboard-trigger',
-    text: (trigger) ->
-      $target = $(trigger.nextElementSibling || trigger.previousElementSibling)
-      $target.data('clipboard-text') || $target.text().trim()
+genericSuccess = (e) ->
+  showTooltip(e.trigger, 'Copied!')
+
+  # Clear the selection and blur the trigger so it loses its border
+  e.clearSelection()
+  $(e.trigger).blur()
 
-  clipboard.on 'success', (e) ->
-    $(e.trigger).
-      tooltip(trigger: 'manual', placement: 'auto bottom', title: 'Copied!').
-      tooltip('show')
+# Safari doesn't support `execCommand`, so instead we inform the user to
+# copy manually.
+#
+# See http://clipboardjs.com/#browser-support
+genericError = (e) ->
+  if /Mac/i.test(navigator.userAgent)
+    key = '&#8984;' # Command
+  else
+    key = 'Ctrl'
 
-    # Clear the selection and blur the trigger so it loses its border
-    e.clearSelection()
-    $(e.trigger).blur()
+  showTooltip(e.trigger, "Press #{key}-C to copy")
 
-    # Manually hide the tooltip after 1 second
-    setTimeout(->
-      $(e.trigger).tooltip('hide')
-    , 1000)
+showTooltip = (target, title) ->
+  $(target).
+    tooltip(
+      container: 'body'
+      html: 'true'
+      placement: 'auto bottom'
+      title: title
+      trigger: 'manual'
+    ).
+    tooltip('show').
+    one('mouseleave', -> $(this).tooltip('hide'))
+
+$ ->
+  clipboard = new Clipboard '[data-clipboard-target], [data-clipboard-text]'
+  clipboard.on 'success', genericSuccess
+  clipboard.on 'error',   genericError
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 951173af5d5bef3dbed97fb6a277d6f1e7e04d5c..4059fc39c67b5d20c2c58e7cfb59b4048f8b836e 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -28,6 +28,8 @@ class Dispatcher
       when 'projects:milestones:new', 'projects:milestones:edit'
         new ZenMode()
         new DropzoneInput($('.milestone-form'))
+      when 'groups:milestones:new'
+        new ZenMode()
       when 'projects:compare:show'
         new Diff()
       when 'projects:issues:new','projects:issues:edit'
diff --git a/app/assets/javascripts/dropzone_input.js.coffee b/app/assets/javascripts/dropzone_input.js.coffee
index 6f789e668af49e5650e21091479d7a8e3c4f3f9f..30a35a04339f6a9fcc680e0ff554a32ea0443275 100644
--- a/app/assets/javascripts/dropzone_input.js.coffee
+++ b/app/assets/javascripts/dropzone_input.js.coffee
@@ -1,3 +1,5 @@
+#= require markdown_preview
+
 class @DropzoneInput
   constructor: (form) ->
     Dropzone.autoDiscover = false
@@ -11,17 +13,14 @@ class @DropzoneInput
     uploadProgress = $("<div class=\"div-dropzone-progress\"></div>")
     btnAlert = "<button type=\"button\"" + alertAttr + ">&times;</button>"
     project_uploads_path = window.project_uploads_path or null
-    markdown_preview_path = window.markdown_preview_path or null
     max_file_size = gon.max_file_size or 10
 
     form_textarea = $(form).find("textarea.markdown-area")
     form_textarea.wrap "<div class=\"div-dropzone\"></div>"
     form_textarea.on 'paste', (event) =>
       handlePaste(event)
-    form_textarea.on "input", ->
-      hideReferencedUsers()
-    form_textarea.on "blur", ->
-      renderMarkdown()
+
+    $(form).setupMarkdownPreview()
 
     form_dropzone = $(form).find('.div-dropzone')
     form_dropzone.parent().addClass "div-dropzone-wrapper"
@@ -34,42 +33,6 @@ class @DropzoneInput
       "opacity": 0
       "display": "none"
 
-    # Preview button
-    $(document).off "click", ".js-md-preview-button"
-    $(document).on "click", ".js-md-preview-button", (e) ->
-      ###
-      Shows the Markdown preview.
-
-      Lets the server render GFM into Html and displays it.
-      ###
-      e.preventDefault()
-      form = $(this).closest("form")
-      # toggle tabs
-      form.find(".js-md-write-button").parent().removeClass "active"
-      form.find(".js-md-preview-button").parent().addClass "active"
-
-      # toggle content
-      form.find(".md-write-holder").hide()
-      form.find(".md-preview-holder").show()
-
-      renderMarkdown()
-
-    # Write button
-    $(document).off "click", ".js-md-write-button"
-    $(document).on "click", ".js-md-write-button", (e) ->
-      ###
-      Shows the Markdown textarea.
-      ###
-      e.preventDefault()
-      form = $(this).closest("form")
-      # toggle tabs
-      form.find(".js-md-write-button").parent().addClass "active"
-      form.find(".js-md-preview-button").parent().removeClass "active"
-
-      # toggle content
-      form.find(".md-write-holder").show()
-      form.find(".md-preview-holder").hide()
-
     dropzone = form_dropzone.dropzone(
       url: project_uploads_path
       dictDefaultMessage: ""
@@ -136,41 +99,6 @@ class @DropzoneInput
 
     child = $(dropzone[0]).children("textarea")
 
-    hideReferencedUsers = ->
-      referencedUsers = form.find(".referenced-users")
-      referencedUsers.hide()
-
-    renderReferencedUsers = (users) ->
-      referencedUsers = form.find(".referenced-users")
-
-      if referencedUsers.length
-        if users.length >= 10
-          referencedUsers.show()
-          referencedUsers.find(".js-referenced-users-count").text users.length
-        else
-          referencedUsers.hide()
-
-    renderMarkdown = ->
-      preview = form.find(".js-md-preview")
-      mdText = form.find(".markdown-area").val()
-      if mdText.trim().length is 0
-        preview.text "Nothing to preview."
-        hideReferencedUsers()
-      else
-        preview.text "Loading..."
-        $.ajax(
-          type: "POST",
-          url: markdown_preview_path,
-          data: {
-            text: mdText
-          },
-          dataType: "json"
-        ).success (data) ->
-          preview.html data.body
-          preview.syntaxHighlight()
-
-          renderReferencedUsers data.references.users
-
     formatLink = (link) ->
       text = "[#{link.alt}](#{link.url})"
       text = "!#{text}" if link.is_image
diff --git a/app/assets/javascripts/issues.js.coffee b/app/assets/javascripts/issues.js.coffee
index 40bb9e9cb0cc45f1d79d96959b0635ba91379986..ac9e022e727870e7344aad984b2bae632cbdd7de 100644
--- a/app/assets/javascripts/issues.js.coffee
+++ b/app/assets/javascripts/issues.js.coffee
@@ -29,7 +29,7 @@
     $('#filter_issue_search').val($('#issue_search').val())
 
   initSelects: ->
-    $("select#update_status").select2(width: 'resolve', dropdownAutoWidth: true)
+    $("select#update_state_event").select2(width: 'resolve', dropdownAutoWidth: true)
     $("select#update_assignee_id").select2(width: 'resolve', dropdownAutoWidth: true)
     $("select#update_milestone_id").select2(width: 'resolve', dropdownAutoWidth: true)
     $("select#label_name").select2(width: 'resolve', dropdownAutoWidth: true)
diff --git a/app/assets/javascripts/markdown_preview.js.coffee b/app/assets/javascripts/markdown_preview.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..98fc8f173401e10487494ad5c025de363baed0dd
--- /dev/null
+++ b/app/assets/javascripts/markdown_preview.js.coffee
@@ -0,0 +1,87 @@
+# MarkdownPreview
+#
+# Handles toggling the "Write" and "Preview" tab clicks, rendering the preview,
+# and showing a warning when more than `x` users are referenced.
+#
+class @MarkdownPreview
+  # Minimum number of users referenced before triggering a warning
+  referenceThreshold: 10
+
+  showPreview: (form) ->
+    preview = form.find('.js-md-preview')
+    mdText  = form.find('textarea.markdown-area').val()
+
+    if mdText.trim().length == 0
+      preview.text('Nothing to preview.')
+      @hideReferencedUsers(form)
+    else
+      preview.text('Loading...')
+      @renderMarkdown mdText, (response) =>
+        preview.html(response.body)
+        preview.syntaxHighlight()
+        @renderReferencedUsers(response.references.users, form)
+
+  renderMarkdown: (text, success) ->
+    return unless window.markdown_preview_path
+
+    $.ajax
+      type: 'POST'
+      url: window.markdown_preview_path
+      data: { text: text }
+      dataType: 'json'
+      success: success
+
+  hideReferencedUsers: (form) ->
+    referencedUsers = form.find('.referenced-users')
+    referencedUsers.hide()
+
+  renderReferencedUsers: (users, form) ->
+    referencedUsers = form.find('.referenced-users')
+
+    if referencedUsers.length
+      if users.length >= @referenceThreshold
+        referencedUsers.show()
+        referencedUsers.find('.js-referenced-users-count').text(users.length)
+      else
+        referencedUsers.hide()
+
+markdownPreview = new MarkdownPreview()
+
+previewButtonSelector = '.js-md-preview-button'
+writeButtonSelector   = '.js-md-write-button'
+
+$.fn.setupMarkdownPreview = ->
+  $form = $(this)
+
+  form_textarea = $form.find('textarea.markdown-area')
+
+  form_textarea.on 'input', -> markdownPreview.hideReferencedUsers($form)
+  form_textarea.on 'blur',  -> markdownPreview.showPreview($form)
+
+$(document).on 'click', previewButtonSelector, (e) ->
+  e.preventDefault()
+
+  $form = $(this).closest('form')
+
+  # toggle tabs
+  $form.find(writeButtonSelector).parent().removeClass('active')
+  $form.find(previewButtonSelector).parent().addClass('active')
+
+  # toggle content
+  $form.find('.md-write-holder').hide()
+  $form.find('.md-preview-holder').show()
+
+  markdownPreview.showPreview($form)
+
+$(document).on 'click', writeButtonSelector, (e) ->
+  e.preventDefault()
+
+  $form = $(this).closest('form')
+
+  # toggle tabs
+  $form.find(writeButtonSelector).parent().addClass('active')
+  $form.find(previewButtonSelector).parent().removeClass('active')
+
+  # toggle content
+  $form.find('.md-write-holder').show()
+  $form.find('.md-preview-holder').hide()
diff --git a/app/assets/javascripts/new_commit_form.js.coffee b/app/assets/javascripts/new_commit_form.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..2e561dea3e1eafe8a52f0bba807d36d8040d2fec
--- /dev/null
+++ b/app/assets/javascripts/new_commit_form.js.coffee
@@ -0,0 +1,21 @@
+class @NewCommitForm
+  constructor: (form) ->
+    @newBranch = form.find('.js-new-branch')
+    @originalBranch = form.find('.js-original-branch')
+    @createMergeRequest = form.find('.js-create-merge-request')
+    @createMergeRequestFormGroup = form.find('.js-create-merge-request-form-group')
+
+    @renderDestination()
+    @newBranch.keyup @renderDestination
+
+  renderDestination: =>
+    different = @newBranch.val() != @originalBranch.val()
+
+    if different
+      @createMergeRequestFormGroup.show()
+      @createMergeRequest.prop('checked', true) unless @wasDifferent
+    else
+      @createMergeRequestFormGroup.hide()
+      @createMergeRequest.prop('checked', false)
+
+    @wasDifferent = different
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index ea75c656bccb5025a2b556913d6b66aefda73c32..7de7632201ddc10da38369321aac4ef08434e267 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -113,13 +113,16 @@ class @Notes
   renderNote: (note) ->
     # render note if it not present in loaded list
     # or skip if rendered
-    if @isNewNote(note)
+    if @isNewNote(note) && !note.award
       @note_ids.push(note.id)
       $('ul.main-notes-list').
         append(note.html).
         syntaxHighlight()
       @initTaskList()
 
+    if note.award
+      awards_handler.addAwardToEmojiBar(note.note, note.emoji_path)
+
   ###
   Check if note does not exists on page
   ###
@@ -255,7 +258,6 @@ class @Notes
   ###
   addNote: (xhr, note, status) =>
     @renderNote(note)
-    @updateVotes()
 
   ###
   Called in response to the new note form being submitted
@@ -473,9 +475,6 @@ class @Notes
     form = $(e.target).closest(".js-discussion-note-form")
     @removeDiscussionNoteForm(form)
 
-  updateVotes: ->
-    true
-
   ###
   Called after an attachment file has been selected.
 
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index 0ea8fffce07179b69b4e81352ce47ef39dc86c1c..ec919f0cd67778ffb95bcc7728ed676c212d4a31 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -1,13 +1,19 @@
 class @Project
   constructor: ->
-    # Git clone panel switcher
-    cloneHolder = $('.git-clone-holder')
-    if cloneHolder.length
-      $('a, button', cloneHolder).click ->
-        $('a, button', cloneHolder).removeClass 'active'
-        $(@).addClass 'active'
-        $('#project_clone', cloneHolder).val $(@).data 'clone'
-        $(".clone").text("").append $(@).data 'clone'
+    # Git protocol switcher
+    $('.js-protocol-switch').click ->
+      return if $(@).hasClass('active')
+
+      # Toggle 'active' for both buttons
+      $('.js-protocol-switch').toggleClass('active')
+
+      url = $(@).data('clone')
+
+      # Update the input field
+      $('#project_clone').val(url)
+
+      # Update the command line instructions
+      $('.clone').text(url)
 
     # Ref switcher
     $('.project-refs-select').on 'change', ->
@@ -39,4 +45,4 @@ class @Project
         when 4 then label = ' On Mention '
       $('#notifications-button').empty().append("<i class='fa fa-bell'></i>" + label + "<i class='fa fa-angle-down'></i>")
       $(@).parents('ul').find('li.active').removeClass 'active'
-      $(@).parent().addClass 'active'
\ No newline at end of file
+      $(@).parent().addClass 'active'
diff --git a/app/assets/javascripts/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/stat_graph_contributors_util.js.coffee
index cfe5508290f14255848d48c9c82b1d036c669442..f5584bcfe4b28c5fbd80486bc56168e86fc49a47 100644
--- a/app/assets/javascripts/stat_graph_contributors_util.js.coffee
+++ b/app/assets/javascripts/stat_graph_contributors_util.js.coffee
@@ -6,7 +6,7 @@ window.ContributorsStatGraphUtil =
     for entry in log
       @add_date(entry.date, total) unless total[entry.date]?
 
-      data = by_author[entry.author_name] #|| by_email[entry.author_email]      
+      data = by_author[entry.author_name] || by_email[entry.author_email]      
       data ?= @add_author(entry, by_author, by_email)
 
       @add_date(entry.date, data) unless data[entry.date]
@@ -95,5 +95,4 @@ window.ContributorsStatGraphUtil =
     if date_range is null || date_range[0] <= new Date(date) <= date_range[1]
       true
     else
-      false
-
+      false
\ No newline at end of file
diff --git a/app/assets/javascripts/users_select.js.coffee b/app/assets/javascripts/users_select.js.coffee
index 9157562a5c58854269743befbd4073205aa3f049..12abf806bfab11ec03a633b8526c3e1e112f203a 100644
--- a/app/assets/javascripts/users_select.js.coffee
+++ b/app/assets/javascripts/users_select.js.coffee
@@ -32,17 +32,15 @@ class @UsersSelect
               if showNullUser
                 nullUser = {
                   name: 'Unassigned',
-                  avatar: null,
-                  username: 'none',
                   id: 0
                 }
                 data.results.unshift(nullUser)
 
               if showAnyUser
+                name = showAnyUser
+                name = 'Any User' if name == true
                 anyUser = {
-                  name: 'Any',
-                  avatar: null,
-                  username: 'none',
+                  name: name,
                   id: null
                 }
                 data.results.unshift(anyUser)
@@ -50,7 +48,6 @@ class @UsersSelect
             if showEmailUser && data.results.length == 0 && query.term.match(/^[^@]+@[^@]+$/)
               emailUser = {
                 name: "Invite \"#{query.term}\"",
-                avatar: null,
                 username: query.term,
                 id: query.term
               }
@@ -58,11 +55,8 @@ class @UsersSelect
 
             query.callback(data)
 
-        initSelection: (element, callback) =>
-          id = $(element).val()
-          if id != "" && id != "0"
-            @user(id, callback)
-
+        initSelection: (args...) =>
+          @initSelection(args...)
         formatResult: (args...) =>
           @formatResult(args...)
         formatSelection: (args...) =>
@@ -71,16 +65,24 @@ class @UsersSelect
         escapeMarkup: (m) -> # we do not want to escape markup since we are displaying html in results
           m
 
+  initSelection: (element, callback) ->
+    id = $(element).val()
+    if id == "0"
+      nullUser = { name: 'Unassigned' }
+      callback(nullUser)
+    else if id != ""
+      @user(id, callback)
+
   formatResult: (user) ->
     if user.avatar_url
       avatar = user.avatar_url
     else
       avatar = gon.default_avatar_url
 
-    "<div class='user-result'>
+    "<div class='user-result #{'no-username' unless user.username}'>
        <div class='user-image'><img class='avatar s24' src='#{avatar}'></div>
        <div class='user-name'>#{user.name}</div>
-       <div class='user-username'>#{user.username}</div>
+       <div class='user-username'>#{user.username || ""}</div>
      </div>"
 
   formatSelection: (user) ->
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 1ec9d2fd84f1fe0e5c69104c7a33c7564f085458..48a4971c8fca834044693063d2ac031688122d0e 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -1,9 +1,9 @@
 @import "framework/fonts";
 @import "framework/variables";
 @import "framework/mixins";
-@import "framework/layout";
 @import 'framework/tw_bootstrap_variables';
 @import 'framework/tw_bootstrap';
+@import "framework/layout";
 
 @import "framework/avatar.scss";
 @import "framework/blocks.scss";
@@ -25,6 +25,7 @@
 @import "framework/markdown_area.scss";
 @import "framework/mobile.scss";
 @import "framework/pagination.scss";
+@import "framework/panels.scss";
 @import "framework/selects.scss";
 @import "framework/sidebar.scss";
 @import "framework/tables.scss";
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 1635df9c97b3081be3dcb1d7d1d742604c26328d..a62c0f62a4c862cd950accfcf367e17ecc5dfc4c 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -68,6 +68,10 @@
   .oneline {
     line-height: 42px;
   }
+
+  > p:last-child {
+    margin-bottom: 0;
+  }
 }
 
 .cover-block {
@@ -112,5 +116,14 @@
     position: absolute;
     top: 10px;
     right: 10px;
+
+    &.left {
+      left: 10px;
+      right: auto;
+    }
   }
 }
+
+.block-connector {
+  margin-top: -1px;
+}
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index ddbacd7fd4100cc4e80b005ec2d1e8992a95e9ba..d2f491daf789133ff1694d1bd79a74088accbc59 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -7,7 +7,7 @@
 
 /** COMMON CLASSES **/
 .prepend-top-10 { margin-top:10px }
-.prepend-top-default { margin-top: $gl-padding; }
+.prepend-top-default { margin-top: $gl-padding !important; }
 .prepend-top-20 { margin-top:20px }
 .prepend-left-10 { margin-left:10px }
 .prepend-left-20 { margin-left:20px }
@@ -52,6 +52,10 @@ pre {
   }
 }
 
+hr {
+  margin: $gl-padding 0;
+}
+
 .dropdown-menu > li > a {
   text-shadow: none;
 }
@@ -64,7 +68,7 @@ pre {
 .dropdown-menu > li > a:hover,
 .dropdown-menu > li > a:focus {
   background: $gl-primary;
-  color: #FFF
+  color: #FFF;
 }
 
 .str-truncated {
@@ -328,15 +332,15 @@ table {
   }
 }
 
+.well {
+  margin-bottom: 0;
+}
+
 .search_box {
   @extend .well;
   text-align: center;
 }
 
-.task-status {
-  margin-left: 10px;
-}
-
 #nprogress .spinner {
   top: 15px !important;
   right: 10px !important;
@@ -429,3 +433,7 @@ table {
 .space-right {
   margin-right: 10px;
 }
+
+.alert, .progress {
+  margin-bottom: $gl-padding;
+}
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index 35db00281e5d26b055c65aac9432fd12ce68b996..6bf2857e83a291f3c4c4a128e82b3c5a4d16eb3c 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -8,7 +8,6 @@
   border: none;
   border-top: 1px solid #E7E9EE;
   border-bottom: 1px solid #E7E9EE;
-  margin-bottom: 1em;
 
   &.readme-holder {
     border-bottom: 0;
@@ -25,7 +24,7 @@
     text-shadow: 0 1px 1px #fff;
     margin: 0;
     text-align: left;
-    padding: 10px 15px;
+    padding: 10px $gl-padding;
 
     .file-actions {
       float: right;
@@ -171,4 +170,3 @@
     }
   }
 }
-
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 0edfe24f19584c8d340f4e79caa861b06901f726..032d343df44e3060ec5fc66499904856839b841d 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -22,9 +22,10 @@ input[type='text'].danger {
 }
 
 .form-actions {
-  padding: 17px 20px 18px;
-  margin-top: 18px;
-  margin-bottom: 18px;
+  margin: -$gl-padding;
+  margin-top: 0;
+  margin-bottom: -$gl-padding;
+  padding: $gl-padding;
   background-color: $background-color;
   border-top: 1px solid $border-color;
 }
@@ -73,6 +74,8 @@ label {
 
 .form-control {
   @include box-shadow(none);
+  height: 42px;
+  padding: 8px $gl-padding;
 }
 
 .wiki-content {
@@ -88,7 +91,19 @@ label {
 }
 
 .input-group {
+  .select2-container {
+    display: table-cell;
+    width: 200px !important;
+  }
   .input-group-addon {
     background-color: #f7f8fa;
   }
+  .input-group-addon:not(:first-child):not(:last-child) {
+    border-left: 0;
+    border-right: 0;
+  }
+}
+
+.help-block {
+  margin-bottom: 0;
 }
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 91e6975e2694eafd7a5a01a3a115199fd3184d1d..02ea91602e8ee3f360fd8fd2ea04968b417a716f 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -118,6 +118,10 @@ header {
       }
     }
   }
+
+  .impersonation i {
+    color: $red-normal;
+  }
 }
 
 @mixin collapsed-header {
diff --git a/app/assets/stylesheets/framework/issue_box.scss b/app/assets/stylesheets/framework/issue_box.scss
index 93377e45e70a52aec9571670bb99b1233e9f978f..f12d68b5a1fe32c8648f87a3d7201665cad526d3 100644
--- a/app/assets/stylesheets/framework/issue_box.scss
+++ b/app/assets/stylesheets/framework/issue_box.scss
@@ -7,8 +7,9 @@
 .issue-box {
   @include border-radius(2px);
 
-  display: inline-block;
-  padding: 10px $gl-padding;
+  display: block;
+  float: left;
+  padding: 0 $gl-padding;
   font-weight: normal;
   margin-right: 10px;
   font-size: $gl-font-size;
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index b91c15d8910f0b2fe2da7dca14863d9414685434..a60940a8beeb135ee7eba6e547199a4d9ab15c53 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -2,10 +2,10 @@ html {
   overflow-y: scroll;
 
   &.touch .tooltip { display: none !important; }
+}
 
-  body {
-    padding-top: $header-height;
-  }
+body {
+  background-color: #EAEBEC !important;
 }
 
 .container {
@@ -18,6 +18,7 @@ html {
 }
 
 .navless-container {
+  padding-top: $header-height;
   margin-top: 30px;
 }
 
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 45f3b5849bfc47cfb05d60790054838c58c232e5..a798ae812e3bbd73d5853877fe116e1ae1570d0c 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -127,3 +127,8 @@ ul.content-list {
   }
 }
 
+.panel > .content-list {
+  li {
+    margin: 0;
+  }
+}
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index 6677f94dafdfe4d2faa2c5c1223816245db1289e..2cd30491bf5de471ee1c144a0f693bb98d7840e6 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -32,3 +32,7 @@
     }
   }
 }
+
+.panel > .gl-pagination {
+  margin: 0;
+}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
new file mode 100644
index 0000000000000000000000000000000000000000..406aff3d72cffaf9325e9f1259f81a196a7098fa
--- /dev/null
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -0,0 +1,15 @@
+.panel {
+  margin-bottom: $gl-padding;
+  
+  .panel-heading {
+    padding: 10px $gl-padding;
+  }
+  .panel-body {
+    padding: $gl-padding;
+
+    .form-actions {
+      margin: -$gl-padding;
+      margin-top: $gl-padding;
+    }
+  }
+}
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index 78fff58d2328dcf57e6d6f4fb462675e05803323..5e5ae3d0af8102333c0091affefcf7ac54184130 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -15,6 +15,16 @@
       border-left: none;
       padding-top: 5px;
     }
+
+    .select2-chosen {
+      color: $gl-text-color;
+    }
+
+    &.select2-default {
+      .select2-chosen {
+        color: #999;
+      }
+    }
   }
 }
 
@@ -23,6 +33,7 @@
   border: 1px solid #e7e9ed;
 }
 
+
 .select2-drop {
   @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px);
   @include border-radius (0px);
@@ -123,10 +134,16 @@
 }
 
 .user-result {
+  min-height: 24px;
+
   .user-image {
     float: left;
   }
-  .user-name {
+
+  &.no-username {
+    .user-name {
+      line-height: 24px;
+    }
   }
 }
 
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index c1b0129c866038faacc0f7cd7e315d90b63d339a..81cda68b94cfc4f1b306d0036181e8540219701d 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -1,4 +1,6 @@
 .page-with-sidebar {
+  padding-top: $header-height;
+
   .sidebar-wrapper {
     position: fixed;
     top: 0;
@@ -18,15 +20,12 @@
 }
 
 .content-wrapper {
-  min-height: 100vh;
   width: 100%;
   padding: 20px;
-  background: #EAEBEC;
 
   .container-fluid {
     background: #FFF;
     padding: $gl-padding;
-    min-height: 90vh;
 
     &.container-blank {
       background: none;
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 66e16e8df75764e9838c46ee4e63d7d5e9df8e7f..793ab3d9bb9bf50e50a3953864233d5ea4f58df5 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -6,6 +6,8 @@
 
 table {
   &.table {
+    margin-bottom: $gl-padding;
+    
     .dropdown-menu a {
       text-decoration: none;
     }
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 99d028d1228ee84bb88394051806b27943db204e..94f0ed761dfbd25cb8307e8ac909de54c3c601b3 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -172,7 +172,7 @@
   }
 
   .panel-body {
-    form {
+    form, pre {
       margin: 0;
     }
 
@@ -190,6 +190,10 @@
     .btn {
       min-width: 124px;
     }
+
+    .btn-clipboard {
+      min-width: 0px;
+    }
   }
 
   &.panel-small {
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index ba0312ba0db5692549945a4f56dc7d928129e0ae..aef338cfa568f2e5dae0007424010710a0671e05 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -181,6 +181,10 @@ body {
   line-height: 1.3;
   font-size: 1.25em;
   font-weight: 600;
+
+  &:last-child {
+    margin-bottom: 0;
+  }
 }
 
 .page-title-empty {
@@ -256,3 +260,9 @@ textarea.js-gfm-input {
 .strikethrough {
   text-decoration: line-through;
 }
+
+h1, h2, h3, h4 {
+  small {
+    color: $gl-gray;
+  }
+}
diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss
index 74dc3e321c1c6e2cfdfffe0faf9c79b20971ebb6..da9965f007af1e409d432381dc49b728cdde31bb 100644
--- a/app/assets/stylesheets/pages/builds.scss
+++ b/app/assets/stylesheets/pages/builds.scss
@@ -21,7 +21,7 @@
 
   .autoscroll-container {
     position: fixed;
-    bottom: 10px;
+    bottom: 20px;
     right: 20px;
     z-index: 100;
   }
@@ -34,7 +34,7 @@
 
     a {
       display: block;
-      margin-bottom: 5px;
+      margin-bottom: 10px;
     }
   }
 
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index fbd7c363de108f414325d5714496668c9827259f..17245d3be7ba4d00a96694e81d2df244e2605303 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -2,10 +2,6 @@
   display: block;
 }
 
-.commit-title{
-  margin-bottom: 10px;
-}
-
 .commit-author, .commit-committer{
   display: block;
   color: #999;
@@ -41,6 +37,8 @@
 .commit-box {
   .commit-title {
     margin: 0;
+    font-size: 23px;
+    color: #313236;
   }
 
   .commit-description {
@@ -56,6 +54,7 @@
 
     li {
       padding: 3px 0px;
+      line-height: 20px;
     }
   }
   .new-file {
@@ -107,16 +106,3 @@
     z-index: 2;
   }
 }
-
-.commit-ci-menu {
-  padding: 0;
-  margin: 0;
-  list-style: none;
-  margin-top: 5px;
-  height: 56px;
-  margin: -16px;
-  padding: 16px;
-  text-align: center;
-  margin-top: 0px;
-  margin-bottom: 2px;
-}
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index e2c521af91ec1dbce8e1135a86f44aa06d17b7ee..39d916cd336002e58b8a7896935a2281553dc317 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -19,48 +19,38 @@
       color: #B94A48;
     }
   }
-  .commit-button-annotation {
-    display: inline-block;
-    margin: 0;
-    padding: 2px;
-
-    > * {
-      float: left;
-    }
-
-    .message {
-      display: inline-block;
-      margin: 5px 8px 0 8px;
-    }
-  }
 
   .file-title {
     @extend .monospace;
+
+    line-height: 42px;
+    padding-top: 7px;
+    padding-bottom: 7px;
   }
 
   .editor-ref {
     background: $background-color;
-    padding: 11px 15px;
+    padding-right: $gl-padding;
     border-right: 1px solid $border-color;
-    display: inline-block;
-    margin: -5px -5px;
+    display: block;
+    float: left;
     margin-right: 10px;
   }
 
   .editor-file-name {
-    .new-file-name {
-      display: inline-block;
-      width: 450px;
-    }
+    @extend .monospace;
+    
+    float: left;
+    margin-right: 10px;
+  }
 
-    .form-control {
-      margin-top: -3px;
-    }
+  .new-file-name {
+    display: inline-block;
+    width: 450px;
+    float: left;
   }
 
-  .form-actions {
-    margin: -$gl-padding;
-    margin-top: 0;
-    padding: $gl-padding
+  .select2 {
+    float: right;
   }
 }
diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss
index 07a38a19fad6b465d3082c6fa9cdaeae48a06704..263993f59a5510eea684ea9ed201d09524fd0930 100644
--- a/app/assets/stylesheets/pages/groups.scss
+++ b/app/assets/stylesheets/pages/groups.scss
@@ -1,8 +1,3 @@
-.new-group-member-holder {
-  margin-top: 50px;
-  padding-top: 20px;
-}
-
 .member-search-form {
   float: left;
 }
@@ -15,4 +10,4 @@
   .form-control {
     height: 42px;
   }
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index abc27a19e3243e188245e4f9ad0901b2f1b71303..9313fa4795ab9b629abb656ce24219d7d1b68853 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -51,11 +51,12 @@
 
 .issuable-details {
   .page-title {
-    margin-top: -15px;
-    padding: 10px 0;
+    margin-top: -$gl-padding;
+    padding: 7px 0;
     margin-bottom: 0;
     color: #5c5d5e;
     font-size: 16px;
+    line-height: 42px;
 
     .author {
       color: #5c5d5e;
@@ -101,3 +102,72 @@
     background-color: $background-color;
   }
 }
+
+.awards {
+  @include clearfix;
+  line-height: 34px;
+  margin: 2px 0;
+
+  .award {
+    @include border-radius(5px);
+
+    border: 1px solid;
+    padding: 0px 10px;
+    float: left;
+    margin: 0 5px;
+    border-color: $border-color;
+    cursor: pointer;
+
+    &.active {
+      border-color: $border-gray-light;
+      background-color: $gray-light;
+
+      .counter {
+        font-weight: bold;
+      }
+    }
+
+    .icon {
+      float: left;
+      margin-right: 10px;
+    }
+
+    .counter {
+      float: left;
+    }
+  }
+
+  .awards-controls {
+    margin-left: 10px;
+    float: left;
+
+    .add-award {
+      font-size: 24px;
+      color: $gl-gray;
+      position: relative;
+      top: 2px;
+
+      &:hover,
+      &:link {
+        text-decoration: none;
+      }
+    }
+
+    .awards-menu {
+      padding: $gl-padding;
+      min-width: 214px;
+
+      > li {
+        cursor: pointer;
+        margin: 5px;
+      }
+    }
+  }
+
+  .awards-menu{
+    li {
+      float: left;
+      margin: 3px;
+    }
+  }
+}
diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss
index 83b866c3a6466f18ffde7359d517774d80a7f373..edd517051364e1324555e29fb1de14aead92c6de 100644
--- a/app/assets/stylesheets/pages/login.scss
+++ b/app/assets/stylesheets/pages/login.scss
@@ -1,5 +1,7 @@
 /* Login Page */
 .login-page {
+  background-color: white;
+  
   .container {
     max-width: 960px;
   }
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 08e4bcdf529ad7354c21cdc5e1252dcac3187cef..017a86bcd9a9a78de98c5dfb1d6dc1bae2b3fe39 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -4,7 +4,6 @@
  */
 .mr-state-widget {
   background: #F7F8FA;
-  margin-bottom: 20px;
   color: $gl-gray;
   border: 1px solid #dce0e6;
   @include border-radius(2px);
@@ -87,7 +86,7 @@
   .mr-widget-body,
   .ci_widget,
   .mr-widget-footer {
-    padding: 15px;
+    padding: $gl-padding;
   }
 
   .normal {
@@ -116,26 +115,8 @@
   }
 }
 
-.merge-request .merge-request-tabs {
-  @include nav-menu;
-  margin: -$gl-padding;
-  padding: $gl-padding;
-  text-align: center;
-  margin-bottom: 1px;
-}
-
-// Mobile
-@media (max-width: 480px) {
-  .merge-request .merge-request-tabs {
-    margin: 0;
-    padding: 0;
-
-    li {
-      a {
-        padding: 0;
-      }
-    }
-  }
+.merge-request-details {
+  margin-bottom: $gl-padding;
 }
 
 .mr_source_commit,
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 268fc995aa72a2d472afaa85077f478355bbd20e..e1a72af00130ea5234072ea58e118c13e1b0800a 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -7,6 +7,7 @@
 }
 .reply-btn {
   @extend .btn-primary;
+  margin: 10px $gl-padding;
 }
 .diff-file .diff-content {
   tr.line_holder:hover {
@@ -38,9 +39,8 @@
 }
 
 .new_note, .edit_note {
-  .buttons {
-    margin-top: 8px;
-    margin-bottom: 3px;
+  .note-form-actions {
+    margin-top: $gl-padding;
   }
 
   .note-preview-holder {
@@ -79,8 +79,8 @@
   padding: $gl-padding;
   margin-left: -$gl-padding;
   margin-right: -$gl-padding;
-  border-right: 1px solid #ECEEF1;
-  border-top: 1px solid #ECEEF1;
+  border-right: 1px solid $border-color;
+  border-top: 1px solid $border-color;
   margin-bottom: -$gl-padding;
 }
 
@@ -150,7 +150,6 @@
 
   .discussion-reply-holder {
     background: $background-color;
-    padding: 10px 15px;
     border-top: 1px solid $border-color;
   }
 }
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d3b10040022e71ce86097277792e8d728dab8054..352f0ba278173adf9e7ee39960b425a440e08793 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -18,10 +18,6 @@
   }
 }
 
-.project-edit-content {
-  padding: 7px;
-}
-
 .project-name-holder {
   .help-inline {
     vertical-align: top;
@@ -30,12 +26,6 @@
 }
 
 .project-home-panel {
-  text-align: center;
-  background: #f7f8fa;
-  margin: -$gl-padding;
-  padding: $gl-padding;
-  padding: 44px 0 17px 0;
-
   .project-identicon-holder {
     margin-bottom: 16px;
 
@@ -90,7 +80,12 @@
   }
 
   .visibility-level-label {
+    @extend .btn;
+    @extend .btn-gray;
+
     color: $gray;
+    cursor: default;
+
     i {
       color: inherit;
     }
@@ -100,7 +95,6 @@
     display: inline-table;
     position: relative;
     top: 17px;
-    margin-bottom: 44px;
   }
 
   .project-repo-buttons {
@@ -178,6 +172,11 @@
     &:active {
       outline: none;
     }
+
+    &.btn-clipboard {
+      padding-left: 15px;
+      padding-right: 15px;
+    }
   }
 
   .active {
@@ -366,7 +365,7 @@ table.table.protected-branches-list tr.no-border {
 
 .project-stats {
   text-align: center;
-  margin-top: 15px;
+  margin-top: $gl-padding;
   margin-bottom: 0;
   padding-top: 10px;
   padding-bottom: 4px;
@@ -552,4 +551,4 @@ pre.light-well {
     z-index: 100;
     position: relative;
   }
-}
\ No newline at end of file
+}
diff --git a/app/assets/stylesheets/pages/snippets.scss b/app/assets/stylesheets/pages/snippets.scss
index 242783a7b7e13b46b94423017d4ba60457f86657..bb74e50151d10dd440cf59c2d313c3bff90ff885 100644
--- a/app/assets/stylesheets/pages/snippets.scss
+++ b/app/assets/stylesheets/pages/snippets.scss
@@ -27,56 +27,22 @@
 }
 
 .snippet-holder {
-  .snippet-details {
-    .page-title {
-      margin-top: -15px;
-      padding: 10px 0;
-      margin-bottom: 0;
-      color: #5c5d5e;
-      font-size: 16px;
-
-      .author {
-        color: #5c5d5e;
-      }
-
-      .snippet-id {
-        color: #5c5d5e;
-      }
-    }
-
-    .snippet-title {
-      margin: 0;
-      font-size: 23px;
-      color: #313236;
-    }
-
-    @media (max-width: $screen-md-max) {
-      .new-snippet-link {
-        display: none;
-      }
-    }
-
-    @media (max-width: $screen-sm-max) {
-      .creator,
-      .page-title .btn-close {
-        display: none;
-      }
-    }
-  }
+  margin-bottom: -$gl-padding;
 
   .file-holder {
     border-top: 0;
   }
 }
 
-
 .snippet-box {
   @include border-radius(2px);
 
-  display: inline-block;
-  padding: 10px $gl-padding;
+  display: block;
+  float: left;
+  padding: 0 $gl-padding;
   font-weight: normal;
   margin-right: 10px;
   font-size: $gl-font-size;
   border: 1px solid;
+  line-height: 40px;
 }
diff --git a/app/assets/stylesheets/pages/wiki.scss b/app/assets/stylesheets/pages/wiki.scss
index dfaeba41cf6603c3d86ca3bfeeb8ac19a9f97310..cdf514197cbe315ebe8fa408acbf796a618ad3a0 100644
--- a/app/assets/stylesheets/pages/wiki.scss
+++ b/app/assets/stylesheets/pages/wiki.scss
@@ -4,3 +4,8 @@
   margin-right: auto;
   padding-right: 7px;
 }
+
+.wiki-last-edit-by {
+  font-size: 80%;
+  font-weight: normal;
+}
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 2f4054eaa117e2fd1403905c77b2e894858dcde7..20bc5173f1d0664e5f1d1d0c1b2b8e307fe8ebf2 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -10,7 +10,7 @@ class AbuseReportsController < ApplicationController
 
     if @abuse_report.save
       if current_application_settings.admin_notification_email.present?
-        AbuseReportMailer.delay.notify(@abuse_report.id)
+        AbuseReportMailer.notify(@abuse_report.id).deliver_later
       end
 
       message = "Thank you for your report. A GitLab administrator will look into it shortly."
diff --git a/app/controllers/admin/application_controller.rb b/app/controllers/admin/application_controller.rb
index 56e24386463db3a95e794faf0ed9ed83df635336..9083bfb41cfdb494dcba62e0bb6ec25186dbdfbd 100644
--- a/app/controllers/admin/application_controller.rb
+++ b/app/controllers/admin/application_controller.rb
@@ -8,4 +8,10 @@ class Admin::ApplicationController < ApplicationController
   def authenticate_admin!
     return render_404 unless current_user.is_admin?
   end
+
+  def authorize_impersonator!
+    if session[:impersonator_id]
+      User.find_by!(username: session[:impersonator_id]).admin?
+    end
+  end
 end
diff --git a/app/controllers/admin/impersonation_controller.rb b/app/controllers/admin/impersonation_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..bf98af786158ce693956a442142a899b48d0cdf2
--- /dev/null
+++ b/app/controllers/admin/impersonation_controller.rb
@@ -0,0 +1,38 @@
+class Admin::ImpersonationController < Admin::ApplicationController
+  skip_before_action :authenticate_admin!, only: :destroy
+
+  before_action :user
+  before_action :authorize_impersonator!
+
+  def create
+    if @user.blocked?
+      flash[:alert] = "You cannot impersonate a blocked user"
+
+      redirect_to admin_user_path(@user)
+    else
+      session[:impersonator_id] = current_user.username
+      session[:impersonator_return_to] = admin_user_path(@user)
+
+      warden.set_user(user, scope: 'user')
+
+      flash[:alert] = "You are impersonating #{user.username}."
+
+      redirect_to root_path
+    end
+  end
+
+  def destroy
+    redirect = session[:impersonator_return_to]
+
+    warden.set_user(user, scope: 'user')
+
+    session[:impersonator_return_to] = nil
+    session[:impersonator_id] = nil
+
+    redirect_to redirect || root_path
+  end
+
+  def user
+    @user ||= User.find_by!(username: params[:id] || session[:impersonator_id])
+  end
+end
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index c63d0793e3141dde9c10d2fb500b374004d04d08..d7c927d444cbc4b43d0d5bfe20f63bb8e2c5a5c8 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -63,12 +63,6 @@ class Admin::UsersController < Admin::ApplicationController
     end
   end
 
-  def login_as
-    sign_in(user)
-    flash[:alert] = "Logged in as #{user.username}"
-    redirect_to root_path
-  end
-
   def disable_two_factor
     user.disable_two_factor!
     redirect_to admin_user_path(user),
diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb
index 202e9da9eee981a876858e17e1ac8741ec50c9ef..77c8dafc012eec88854d5efc7bf763b7b4406d2b 100644
--- a/app/controllers/autocomplete_controller.rb
+++ b/app/controllers/autocomplete_controller.rb
@@ -1,41 +1,15 @@
 class AutocompleteController < ApplicationController
   skip_before_action :authenticate_user!, only: [:users]
+  before_action :find_users, only: [:users]
 
   def users
-    begin
-      @users =
-        if params[:project_id].present?
-          project = Project.find(params[:project_id])
-
-          if can?(current_user, :read_project, project)
-            project.team.users
-          end
-        elsif params[:group_id]
-          group = Group.find(params[:group_id])
-
-          if can?(current_user, :read_group, group)
-            group.users
-          end
-        elsif current_user
-          User.all
-        end
-    rescue ActiveRecord::RecordNotFound
-      if current_user
-        return render json: {}, status: 404
-      end
-    end
-
-    if @users.nil? && current_user.nil?
-      authenticate_user!
-    end
-
     @users ||= User.none
     @users = @users.search(params[:search]) if params[:search].present?
     @users = @users.active
     @users = @users.reorder(:name)
     @users = @users.page(params[:page]).per(PER_PAGE)
 
-    unless params[:search].present?
+    if params[:search].blank?
       # Include current user if available to filter by "Me"
       if params[:current_user] && current_user
         @users = [*@users, current_user].uniq
@@ -49,4 +23,25 @@ class AutocompleteController < ApplicationController
     @user = User.find(params[:id])
     render json: @user, only: [:name, :username, :id], methods: [:avatar_url]
   end
+
+  private
+
+  def find_users
+    @users =
+      if params[:project_id].present?
+        project = Project.find(params[:project_id])
+        return render_404 unless can?(current_user, :read_project, project)
+
+        project.team.users
+      elsif params[:group_id].present?
+        group = Group.find(params[:group_id])
+        return render_404 unless can?(current_user, :read_group, group)
+
+        group.users
+      elsif current_user
+        User.all
+      else
+        User.none
+      end
+  end
 end
diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb
index 24dd1b5c93aba542c1edb9c4a96ce036886f7bd8..a4f6aff49b4556930bcbd6ec3fda964c466d376b 100644
--- a/app/controllers/ci/lints_controller.rb
+++ b/app/controllers/ci/lints_controller.rb
@@ -15,10 +15,10 @@ module Ci
         @builds = @config_processor.builds
         @status = true
       end
-    rescue Ci::GitlabCiYamlProcessor::ValidationError => e
+    rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
       @error = e.message
       @status = false
-    rescue Exception
+    rescue
       @error = "Undefined error"
       @status = false
     end
diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb
index 809b44387ba0df263dc335101cc8dd884367a176..8406399fb60a3cb98d7d1279b3f9feb19af31512 100644
--- a/app/controllers/ci/projects_controller.rb
+++ b/app/controllers/ci/projects_controller.rb
@@ -26,10 +26,6 @@ module Ci
       redirect_to namespace_project_runners_path(project.gl_project.namespace, project.gl_project)
     end
 
-    def dumped_yaml
-      send_data @project.generated_yaml_config, filename: '.gitlab-ci.yml'
-    end
-
     protected
 
     def project
diff --git a/app/controllers/concerns/creates_merge_request_for_commit.rb b/app/controllers/concerns/creates_merge_request_for_commit.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c75278221585a95a18e591f63c1e7839727ace7a
--- /dev/null
+++ b/app/controllers/concerns/creates_merge_request_for_commit.rb
@@ -0,0 +1,28 @@
+module CreatesMergeRequestForCommit
+  extend ActiveSupport::Concern
+
+  def new_merge_request_path
+    if @project.forked?
+      target_project = @project.forked_from_project || @project
+      target_branch = target_project.repository.root_ref
+    else
+      target_project = @project
+      target_branch = @ref
+    end
+
+    new_namespace_project_merge_request_path(
+      @project.namespace,
+      @project,
+      merge_request: {
+        source_project_id: @project.id,
+        target_project_id: target_project.id,
+        source_branch: @new_branch,
+        target_branch: target_branch
+      }
+    )
+  end
+
+  def create_merge_request?
+    params[:create_merge_request] && @new_branch != @ref
+  end
+end
diff --git a/app/controllers/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b428249acd3eed58de5dbdad1fb8a033ac689d81
--- /dev/null
+++ b/app/controllers/concerns/global_milestones.rb
@@ -0,0 +1,19 @@
+module GlobalMilestones
+  extend ActiveSupport::Concern
+
+  def milestones
+    @milestones = MilestonesFinder.new.execute(@projects, params)
+    @milestones = GlobalMilestone.build_collection(@milestones)
+    @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE)
+  end
+
+  def milestone
+    milestones = Milestone.of_projects(@projects).where(title: params[:title])
+
+    if milestones.present?
+      @milestone = GlobalMilestone.new(params[:title], milestones)
+    else
+      render_404
+    end
+  end
+end
diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb
new file mode 100644
index 0000000000000000000000000000000000000000..effd47219496a56036661fdfc8d869a9b4e87ec9
--- /dev/null
+++ b/app/controllers/concerns/issues_action.rb
@@ -0,0 +1,14 @@
+module IssuesAction
+  extend ActiveSupport::Concern
+
+  def issues
+    @issues = get_issues_collection
+    @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE)
+    @issues = @issues.preload(:author, :project)
+
+    respond_to do |format|
+      format.html
+      format.atom { render layout: false }
+    end
+  end
+end
diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f7a25111db96d49c4d045cb1ed57a8c55a0fe2c1
--- /dev/null
+++ b/app/controllers/concerns/merge_requests_action.rb
@@ -0,0 +1,9 @@
+module MergeRequestsAction
+  extend ActiveSupport::Concern
+
+  def merge_requests
+    @merge_requests = get_merge_requests_collection
+    @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE)
+    @merge_requests = @merge_requests.preload(:author, :target_project)
+  end
+end
diff --git a/app/controllers/dashboard/milestones_controller.rb b/app/controllers/dashboard/milestones_controller.rb
index 53896d4f2c73a356334d9e88b36f96b22c3aa71b..2bdce0f8a0025326c4e6742931fb262b1878cc63 100644
--- a/app/controllers/dashboard/milestones_controller.rb
+++ b/app/controllers/dashboard/milestones_controller.rb
@@ -1,34 +1,19 @@
 class Dashboard::MilestonesController < Dashboard::ApplicationController
-  before_action :load_projects
+  include GlobalMilestones
+
+  before_action :projects
+  before_action :milestones, only: [:index]
+  before_action :milestone, only: [:show]
 
   def index
-    project_milestones = case params[:state]
-                         when 'all'; state
-                         when 'closed'; state('closed')
-                         else state('active')
-                         end
-    @dashboard_milestones = Milestones::GroupService.new(project_milestones).execute
-    @dashboard_milestones = Kaminari.paginate_array(@dashboard_milestones).page(params[:page]).per(PER_PAGE)
   end
 
   def show
-    project_milestones = Milestone.where(project_id: @projects).order("due_date ASC")
-    @dashboard_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
   end
 
   private
 
-  def load_projects
-    @projects = current_user.authorized_projects.sorted_by_activity.non_archived
-  end
-
-  def title
-    params[:title]
-  end
-
-  def state(state = nil)
-    conditions = { project_id: @projects }
-    conditions.reverse_merge!(state: state) if state
-    Milestone.where(conditions).order("title ASC")
+  def projects
+    @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
   end
 end
diff --git a/app/controllers/dashboard_controller.rb b/app/controllers/dashboard_controller.rb
index 4ebb3d7276ed13b9ff800209bed569e8617369c9..087da935087149bbf6d76bb39ca136388c4f3765 100644
--- a/app/controllers/dashboard_controller.rb
+++ b/app/controllers/dashboard_controller.rb
@@ -1,25 +1,12 @@
 class DashboardController < Dashboard::ApplicationController
+  include IssuesAction
+  include MergeRequestsAction
+
   before_action :event_filter, only: :activity
+  before_action :projects, only: [:issues, :merge_requests]
 
   respond_to :html
 
-  def merge_requests
-    @merge_requests = get_merge_requests_collection
-    @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
-    @merge_requests = @merge_requests.preload(:author, :target_project)
-  end
-
-  def issues
-    @issues = get_issues_collection
-    @issues = @issues.page(params[:page]).per(PER_PAGE)
-    @issues = @issues.preload(:author, :project)
-
-    respond_to do |format|
-      format.html
-      format.atom { render layout: false }
-    end
-  end
-
   def activity
     @last_push = current_user.recent_push
 
@@ -47,4 +34,8 @@ class DashboardController < Dashboard::ApplicationController
     @events = @event_filter.apply_filter(@events).with_associations
     @events = @events.limit(20).offset(params[:offset] || 0)
   end
+
+  def projects
+    @projects ||= current_user.authorized_projects.sorted_by_activity.non_archived
+  end
 end
diff --git a/app/controllers/groups/application_controller.rb b/app/controllers/groups/application_controller.rb
index 6878d4bc07ecbc218f2c99529189888f52a7e4fd..be801858eafed5db195b01c1ede0a3bf83175282 100644
--- a/app/controllers/groups/application_controller.rb
+++ b/app/controllers/groups/application_controller.rb
@@ -1,8 +1,13 @@
 class Groups::ApplicationController < ApplicationController
   layout 'group'
+  before_action :group
 
   private
-  
+
+  def group
+    @group ||= Group.find_by(path: params[:group_id])
+  end
+
   def authorize_read_group!
     unless @group and can?(current_user, :read_group, @group)
       if current_user.nil?
@@ -12,13 +17,13 @@ class Groups::ApplicationController < ApplicationController
       end
     end
   end
-  
+
   def authorize_admin_group!
     unless can?(current_user, :admin_group, group)
       return render_404
     end
   end
-  
+
   def authorize_admin_group_member!
     unless can?(current_user, :admin_group_member, group)
       return render_403
diff --git a/app/controllers/groups/avatars_controller.rb b/app/controllers/groups/avatars_controller.rb
index 6aa64222f7789601cfbfbca55ed02ee5b69d8cb1..76c87366baad198859a3dd9dfd77849c45437fb2 100644
--- a/app/controllers/groups/avatars_controller.rb
+++ b/app/controllers/groups/avatars_controller.rb
@@ -1,8 +1,6 @@
-class Groups::AvatarsController < ApplicationController
+class Groups::AvatarsController < Groups::ApplicationController
   def destroy
-    @group = Group.find_by(path: params[:group_id])
     @group.remove_avatar!
-
     @group.save
 
     redirect_to edit_group_path(@group)
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index 91518c44a988e900a00ccdf6e5cd93398429dab7..0e902c4bb4347958201b5430b7c6ece70dc068c5 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -1,11 +1,9 @@
 class Groups::GroupMembersController < Groups::ApplicationController
   skip_before_action :authenticate_user!, only: [:index]
-  before_action :group
 
   # Authorize
   before_action :authorize_read_group!
-  before_action :authorize_admin_group!, except: [:index, :leave]
-  before_action :authorize_admin_group_member!, only: [:create, :resend_invite]
+  before_action :authorize_admin_group_member!, except: [:index, :leave]
 
   def index
     @project = @group.projects.find(params[:project_id]) if params[:project_id]
@@ -18,7 +16,8 @@ class Groups::GroupMembersController < Groups::ApplicationController
     end
 
     @members = @members.order('access_level DESC').page(params[:page]).per(50)
-    @group_member = GroupMember.new
+
+    @group_member = @group.group_members.new
   end
 
   def create
@@ -28,24 +27,23 @@ class Groups::GroupMembersController < Groups::ApplicationController
   end
 
   def update
-    @member = @group.group_members.find(params[:id])
+    @group_member = @group.group_members.find(params[:id])
 
-    return render_403 unless can?(current_user, :update_group_member, @member)
+    return render_403 unless can?(current_user, :update_group_member, @group_member)
 
-    @member.update_attributes(member_params)
+    @group_member.update_attributes(member_params)
   end
 
   def destroy
     @group_member = @group.group_members.find(params[:id])
 
-    if can?(current_user, :destroy_group_member, @group_member)  # May fail if last owner.
-      @group_member.destroy
-      respond_to do |format|
-        format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
-        format.js { render nothing: true }
-      end
-    else
-      return render_403
+    return render_403 unless can?(current_user, :destroy_group_member, @group_member)
+
+    @group_member.destroy
+
+    respond_to do |format|
+      format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
+      format.js { render nothing: true }
     end
   end
 
@@ -64,10 +62,11 @@ class Groups::GroupMembersController < Groups::ApplicationController
   end
 
   def leave
-    @group_member = @group.group_members.where(user_id: current_user.id).first
+    @group_member = @group.group_members.find_by(user_id: current_user)
 
     if can?(current_user, :destroy_group_member, @group_member)
       @group_member.destroy
+
       redirect_to(dashboard_groups_path, notice: "You left #{group.name} group.")
     else
       if @group.last_owner?(current_user)
@@ -80,10 +79,6 @@ class Groups::GroupMembersController < Groups::ApplicationController
 
   protected
 
-  def group
-    @group ||= Group.find_by(path: params[:group_id])
-  end
-
   def member_params
     params.require(:group_member).permit(:access_level, :user_id)
   end
diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb
index 669f7f3126df188bb34056a3cd7246506e5f9b1a..10233222ee1b8f76c6a78fe1a29c05cfbf33122a 100644
--- a/app/controllers/groups/milestones_controller.rb
+++ b/app/controllers/groups/milestones_controller.rb
@@ -1,54 +1,55 @@
 class Groups::MilestonesController < Groups::ApplicationController
-  before_action :authorize_group_milestone!, only: :update
+  include GlobalMilestones
+
+  before_action :projects
+  before_action :milestones, only: [:index]
+  before_action :milestone, only: [:show, :update]
+  before_action :authorize_group_milestone!, only: [:create, :update]
 
   def index
-    project_milestones = case params[:state]
-                         when 'all'; state
-                         when 'closed'; state('closed')
-                         else state('active')
-                         end
-    @group_milestones = Milestones::GroupService.new(project_milestones).execute
-    @group_milestones = Kaminari.paginate_array(@group_milestones).page(params[:page]).per(PER_PAGE)
   end
 
-  def show
-    project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
-    @group_milestone = Milestones::GroupService.new(project_milestones).milestone(title)
+  def new
+    @milestone = Milestone.new
   end
 
-  def update
-    project_milestones = Milestone.where(project_id: group.projects).order("due_date ASC")
-    @group_milestones = Milestones::GroupService.new(project_milestones).milestone(title)
+  def create
+    project_ids = params[:milestone][:project_ids]
+    title = milestone_params[:title]
 
-    @group_milestones.milestones.each do |milestone|
-      Milestones::UpdateService.new(milestone.project, current_user, params[:milestone]).execute(milestone)
+    @group.projects.where(id: project_ids).each do |project|
+      Milestones::CreateService.new(project, current_user, milestone_params).execute
     end
 
-    respond_to do |format|
-      format.js
-      format.html do
-        redirect_to group_milestones_path(group)
-      end
+    redirect_to milestone_path(title)
+  end
+
+  def show
+  end
+
+  def update
+    @milestone.milestones.each do |milestone|
+      Milestones::UpdateService.new(milestone.project, current_user, milestone_params).execute(milestone)
     end
+
+    redirect_back_or_default(default: milestone_path(@milestone.title))
   end
 
   private
 
-  def group
-    @group ||= Group.find_by(path: params[:group_id])
+  def authorize_group_milestone!
+    return render_404 unless can?(current_user, :admin_milestones, group)
   end
 
-  def title
-    params[:title]
+  def milestone_params
+    params.require(:milestone).permit(:title, :description, :due_date, :state_event)
   end
 
-  def state(state = nil)
-    conditions = { project_id: group.projects }
-    conditions.reverse_merge!(state: state) if state
-    Milestone.where(conditions).order("title ASC")
+  def milestone_path(title)
+    group_milestone_path(@group, title.parameterize, title: title)
   end
 
-  def authorize_group_milestone!
-    return render_404 unless can?(current_user, :admin_group, group)
+  def projects
+    @projects ||= @group.projects
   end
 end
diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb
index fb4eb094f274627c6e5c7a30348787f5d86d109f..fb26a4e6fc335a3780394b5d8388ee32268999e6 100644
--- a/app/controllers/groups_controller.rb
+++ b/app/controllers/groups_controller.rb
@@ -1,4 +1,7 @@
 class GroupsController < Groups::ApplicationController
+  include IssuesAction
+  include MergeRequestsAction
+
   skip_before_action :authenticate_user!, only: [:show, :issues, :merge_requests]
   respond_to :html
   before_action :group, except: [:new, :create]
@@ -53,23 +56,6 @@ class GroupsController < Groups::ApplicationController
     end
   end
 
-  def merge_requests
-    @merge_requests = get_merge_requests_collection
-    @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE)
-    @merge_requests = @merge_requests.preload(:author, :target_project)
-  end
-
-  def issues
-    @issues = get_issues_collection
-    @issues = @issues.page(params[:page]).per(PER_PAGE)
-    @issues = @issues.preload(:author, :project)
-
-    respond_to do |format|
-      format.html
-      format.atom { render layout: false }
-    end
-  end
-
   def edit
   end
 
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 519d6d6127e44e596f1d2be05e5871e361eff3df..d3f926b62bcdaabdfaa9ddb37864c56aeba78a44 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -29,7 +29,7 @@ class Projects::ApplicationController < ApplicationController
   private
 
   def ci_enabled
-    return render_404 unless @project.gitlab_ci?
+    return render_404 unless @project.builds_enabled?
   end
 
   def ci_project
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index 93738aa1ee56eff11db08449cae911d948e20880..31a33bfd237ca896332cd538f8424126f6b4d89a 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -1,6 +1,7 @@
 # Controller for viewing a file's blame
 class Projects::BlobController < Projects::ApplicationController
   include ExtractsPath
+  include CreatesMergeRequestForCommit
   include ActionView::Helpers::SanitizeHelper
 
   # Raised when given an invalid file path
@@ -22,21 +23,9 @@ class Projects::BlobController < Projects::ApplicationController
   end
 
   def create
-    result = Files::CreateService.new(@project, current_user, @commit_params).execute
-
-    if result[:status] == :success
-      flash[:notice] = "The changes have been successfully committed"
-      respond_to do |format|
-        format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) }
-        format.json { render json: { message: "success", filePath: namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @file_path)) } }
-      end
-    else
-      flash[:alert] = result[:message]
-      respond_to do |format|
-        format.html { render :new }
-        format.json { render json: { message: "failed", filePath: namespace_project_blob_path(@project.namespace, @project, @id) } }
-      end
-    end
+    create_commit(Files::CreateService, success_path: after_create_path,
+                                        failure_view: :new,
+                                        failure_path: namespace_project_new_blob_path(@project.namespace, @project, @ref))
   end
 
   def show
@@ -47,21 +36,9 @@ class Projects::BlobController < Projects::ApplicationController
   end
 
   def update
-    result = Files::UpdateService.new(@project, current_user, @commit_params).execute
-
-    if result[:status] == :success
-      flash[:notice] = "Your changes have been successfully committed"
-      respond_to do |format|
-        format.html { redirect_to after_edit_path }
-        format.json { render json: { message: "success", filePath: after_edit_path } }
-      end
-    else
-      flash[:alert] = result[:message]
-      respond_to do |format|
-        format.html { render :edit }
-        format.json { render json: { message: "failed", filePath: namespace_project_new_blob_path(@project.namespace, @project, @id) } }
-      end
-    end
+    create_commit(Files::UpdateService, success_path: after_edit_path,
+                                        failure_view: :edit,
+                                        failure_path: namespace_project_blob_path(@project.namespace, @project, @id))
   end
 
   def preview
@@ -77,7 +54,7 @@ class Projects::BlobController < Projects::ApplicationController
 
     if result[:status] == :success
       flash[:notice] = "Your changes have been successfully committed"
-      redirect_to namespace_project_tree_path(@project.namespace, @project, @target_branch)
+      redirect_to after_destroy_path
     else
       flash[:alert] = result[:message]
       render :show
@@ -131,15 +108,51 @@ class Projects::BlobController < Projects::ApplicationController
     render_404
   end
 
+  def create_commit(service, success_path:, failure_view:, failure_path:)
+    result = service.new(@project, current_user, @commit_params).execute
+
+    if result[:status] == :success
+      flash[:notice] = "Your changes have been successfully committed"
+      respond_to do |format|
+        format.html { redirect_to success_path }
+        format.json { render json: { message: "success", filePath: success_path } }
+      end
+    else
+      flash[:alert] = result[:message]
+      respond_to do |format|
+        format.html { render failure_view }
+        format.json { render json: { message: "failed", filePath: failure_path } }
+      end
+    end
+  end
+
+  def after_create_path
+    @after_create_path ||=
+      if create_merge_request?
+        new_merge_request_path
+      else
+        namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @file_path))
+      end
+  end
+
   def after_edit_path
     @after_edit_path ||=
-      if from_merge_request
+      if create_merge_request?
+        new_merge_request_path
+      elsif from_merge_request && @new_branch == @ref
         diffs_namespace_project_merge_request_path(from_merge_request.target_project.namespace, from_merge_request.target_project, from_merge_request) +
           "#file-path-#{hexdigest(@path)}"
-      elsif @target_branch.present?
-        namespace_project_blob_path(@project.namespace, @project, File.join(@target_branch, @path))
       else
-        namespace_project_blob_path(@project.namespace, @project, @id)
+        namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @path))
+      end
+  end
+
+  def after_destroy_path
+    @after_destroy_path ||=
+      if create_merge_request?
+        new_merge_request_path
+      else
+        namespace_project_tree_path(@project.namespace, @project, @new_branch)
       end
   end
 
@@ -154,7 +167,7 @@ class Projects::BlobController < Projects::ApplicationController
 
   def editor_variables
     @current_branch = @ref
-    @target_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
+    @new_branch = params[:new_branch].present? ? sanitized_new_branch_name : @ref
 
     @file_path =
       if action_name.to_s == 'create'
@@ -174,7 +187,7 @@ class Projects::BlobController < Projects::ApplicationController
     @commit_params = {
       file_path: @file_path,
       current_branch: @current_branch,
-      target_branch: @target_branch,
+      target_branch: @new_branch,
       commit_message: params[:commit_message],
       file_content: params[:content],
       file_content_encoding: params[:encoding]
diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb
index 8788442095228a8df3775a39374a6664f4899087..4db3b3bf23db19e3cec742473fd5b3531a28e431 100644
--- a/app/controllers/projects/branches_controller.rb
+++ b/app/controllers/projects/branches_controller.rb
@@ -3,7 +3,7 @@ class Projects::BranchesController < Projects::ApplicationController
   # Authorize
   before_action :require_non_empty_project
   before_action :authorize_download_code!
-  before_action :authorize_push_code!, only: [:create, :destroy]
+  before_action :authorize_push_code!, only: [:new, :create, :destroy]
 
   def index
     @sort = params[:sort] || 'name'
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index deefdd766678ce6dc16999c3f4902c37755639bf..3f137440e28f5e8c5a6a798478518f1415fed125 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -67,7 +67,12 @@ class Projects::CommitController < Projects::ApplicationController
   end
 
   def define_show_vars
-    @diffs = commit.diffs
+    if params[:w].to_i == 1
+      @diffs = commit.diffs({ ignore_whitespace_change: true })
+    else
+      @diffs = commit.diffs
+    end
+
     @notes_count = commit.notes.count
     
     @builds = ci_commit.builds if ci_commit
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 71aaad1fad61d565a67250afd6b55f1ad17bd900..5200d609cc98c458f19c438fa7c7630456e59573 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -12,15 +12,16 @@ class Projects::CompareController < Projects::ApplicationController
   def show
     base_ref = Addressable::URI.unescape(params[:from])
     @ref = head_ref = Addressable::URI.unescape(params[:to])
+    diff_options = { ignore_whitespace_change: true } if params[:w] == '1'
 
     compare_result = CompareService.new.
-      execute(@project, head_ref, @project, base_ref)
+      execute(@project, head_ref, @project, base_ref, diff_options)
 
     if compare_result
       @commits = Commit.decorate(compare_result.commits, @project)
       @diffs = compare_result.diffs
-      @commit = @commits.last
-      @first_commit = @commits.first
+      @commit = @project.commit(head_ref)
+      @first_commit = @project.commit(base_ref)
       @line_notes = []
     end
   end
diff --git a/app/controllers/projects/imports_controller.rb b/app/controllers/projects/imports_controller.rb
index 066b66014f8725ce6c7dd50db8d472d4208e1957..fb8788f0818bbbd7d4b022b1dab647ec45035460 100644
--- a/app/controllers/projects/imports_controller.rb
+++ b/app/controllers/projects/imports_controller.rb
@@ -28,8 +28,8 @@ class Projects::ImportsController < Projects::ApplicationController
       if @project.import_finished?
         redirect_to(project_path(@project)) and return
       else
-        redirect_to new_namespace_project_import_path(@project.namespace,
-                                                      @project) && return
+        redirect_to(new_namespace_project_import_path(@project.namespace,
+                                                      @project)) and return
       end
     end
   end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index e767efbdc0cfd168c51670ac5b1b6fcad6b7feac..ae474cf8d687435cfce313e57aa0f309cab05579 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -60,7 +60,7 @@ class Projects::IssuesController < Projects::ApplicationController
   def show
     @participants = @issue.participants(current_user)
     @note = @project.notes.new(noteable: @issue)
-    @notes = @issue.notes.with_associations.fresh
+    @notes = @issue.notes.nonawards.with_associations.fresh
     @noteable = @issue
 
     respond_with(@issue)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index b0788a2d073e8ceca450aa50872cecd50cadcfb6..3f47f2ddb2cb6a661a4596106cf72f716c41d049 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -254,7 +254,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
     # Build a note object for comment form
     @note = @project.notes.new(noteable: @merge_request)
-    @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
+    @notes = @merge_request.mr_and_commit_notes.nonawards.inc_author.fresh
     @discussions = Note.discussions_from_notes(@notes)
     @noteable = @merge_request
 
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 41cd08c93c60bfed5fa7ac6f0e14c85055f86162..5ac18446aa7dccb01bf63685da25ee1d7f3b8cd3 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -3,7 +3,7 @@ class Projects::NotesController < Projects::ApplicationController
   before_action :authorize_read_note!
   before_action :authorize_create_note!, only: [:create]
   before_action :authorize_admin_note!, only: [:update, :destroy]
-  before_action :find_current_user_notes, except: [:destroy, :delete_attachment]
+  before_action :find_current_user_notes, except: [:destroy, :delete_attachment, :award_toggle]
 
   def index
     current_fetched_at = Time.now.to_i
@@ -58,6 +58,30 @@ class Projects::NotesController < Projects::ApplicationController
     end
   end
 
+  def award_toggle
+    noteable = if note_params[:noteable_type] == "issue"
+                 project.issues.find(note_params[:noteable_id])
+               else
+                 project.merge_requests.find(note_params[:noteable_id])
+               end
+
+    data = {
+      author: current_user,
+      is_award: true,
+      note: note_params[:note].gsub(":", '')
+    }
+
+    note = noteable.notes.find_by(data)
+
+    if note
+      note.destroy
+    else
+      Notes::CreateService.new(project, current_user, note_params).execute
+    end
+
+    render json: { ok: true }
+  end
+
   private
 
   def note
@@ -111,6 +135,9 @@ class Projects::NotesController < Projects::ApplicationController
       id: note.id,
       discussion_id: note.discussion_id,
       html: note_to_html(note),
+      award: note.is_award,
+      emoji_path: note.is_award ? view_context.image_url(::AwardEmoji.path_to_emoji_image(note.note)) : "",
+      note: note.note,
       discussion_html: note_to_discussion_html(note),
       discussion_with_diff_html: note_to_discussion_with_diff_html(note)
     }
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 9de5269cd2520abaea1b308e694dfd3999754f9d..07eb94e4f48dd9967b7ac435f73702f56e661c30 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -1,6 +1,6 @@
 class Projects::ProjectMembersController < Projects::ApplicationController
   # Authorize
-  before_action :authorize_admin_project!, except: :leave
+  before_action :authorize_admin_project_member!, except: :leave
 
   def index
     @project_members = @project.project_members
@@ -29,10 +29,6 @@ class Projects::ProjectMembersController < Projects::ApplicationController
     @project_member = @project.project_members.new
   end
 
-  def new
-    @project_member = @project.project_members.new
-  end
-
   def create
     @project.team.add_users(params[:user_ids].split(','), params[:access_level], current_user)
 
@@ -41,11 +37,17 @@ class Projects::ProjectMembersController < Projects::ApplicationController
 
   def update
     @project_member = @project.project_members.find(params[:id])
+
+    return render_403 unless can?(current_user, :update_project_member, @project_member)
+
     @project_member.update_attributes(member_params)
   end
 
   def destroy
     @project_member = @project.project_members.find(params[:id])
+
+    return render_403 unless can?(current_user, :destroy_project_member, @project_member)
+
     @project_member.destroy
 
     respond_to do |format|
@@ -71,16 +73,22 @@ class Projects::ProjectMembersController < Projects::ApplicationController
   end
 
   def leave
-    if @project.namespace == current_user.namespace
-      message = 'You can not leave your own project. Transfer or delete the project.'
-      return redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
-    end
+    @project_member = @project.project_members.find_by(user_id: current_user)
 
-    @project.project_members.find_by(user_id: current_user).destroy
+    if can?(current_user, :destroy_project_member, @project_member)
+      @project_member.destroy
 
-    respond_to do |format|
-      format.html { redirect_to dashboard_projects_path }
-      format.js { render nothing: true }
+      respond_to do |format|
+        format.html { redirect_to dashboard_projects_path, notice: "You left the project." }
+        format.js { render nothing: true }
+      end
+    else
+      if current_user == @project.owner
+        message = 'You can not leave your own project. Transfer or delete the project.'
+        redirect_back_or_default(default: { action: 'index' }, options: { alert: message })
+      else
+        render_403
+      end
     end
   end
 
diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb
index cb39c2b878283dba5663642c7da542caab4dcbea..280fe12cc7cd43c7c543bf7c5ee8f2176949ff9f 100644
--- a/app/controllers/projects/tags_controller.rb
+++ b/app/controllers/projects/tags_controller.rb
@@ -2,7 +2,7 @@ class Projects::TagsController < Projects::ApplicationController
   # Authorize
   before_action :require_non_empty_project
   before_action :authorize_download_code!
-  before_action :authorize_push_code!, only: [:create]
+  before_action :authorize_push_code!, only: [:new, :create]
   before_action :authorize_admin_project!, only: [:destroy]
 
   def index
diff --git a/app/controllers/projects/tree_controller.rb b/app/controllers/projects/tree_controller.rb
index bdcb1a3e297ee178eb58f15157f06f61db037d81..8f272ad1281fef7306c646248ff67acf1f18465d 100644
--- a/app/controllers/projects/tree_controller.rb
+++ b/app/controllers/projects/tree_controller.rb
@@ -1,6 +1,7 @@
 # Controller for viewing a repository's file structure
 class Projects::TreeController < Projects::ApplicationController
   include ExtractsPath
+  include CreatesMergeRequestForCommit
   include ActionView::Helpers::SanitizeHelper
 
   before_action :require_non_empty_project, except: [:new, :create]
@@ -43,7 +44,7 @@ class Projects::TreeController < Projects::ApplicationController
     if result && result[:status] == :success
       flash[:notice] = "The directory has been successfully created"
       respond_to do |format|
-        format.html { redirect_to namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name)) }
+        format.html { redirect_to after_create_dir_path }
       end
     else
       flash[:alert] = message
@@ -53,6 +54,8 @@ class Projects::TreeController < Projects::ApplicationController
     end
   end
 
+  private
+
   def assign_dir_vars
     @new_branch = params[:new_branch].present? ? sanitize(strip_tags(params[:new_branch])) : @ref
     @dir_name = File.join(@path, params[:dir_name])
@@ -63,4 +66,12 @@ class Projects::TreeController < Projects::ApplicationController
       commit_message: params[:commit_message],
     }
   end
+
+  def after_create_dir_path
+    if create_merge_request?
+      new_merge_request_path
+    else
+      namespace_project_blob_path(@project.namespace, @project, File.join(@new_branch, @dir_name))
+    end
+  end
 end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 00d13a83ce8372b44a099389564baa534be8a981..10c75370d7bdb66f16d85ae27b07f608836d938d 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -72,8 +72,7 @@ class ProjectsController < ApplicationController
   def remove_fork
     return access_denied! unless can?(current_user, :remove_fork_project, @project)
 
-    if @project.forked?
-      @project.forked_project_link.destroy
+    if @project.unlink_fork
       flash[:notice] = 'The fork relationship has been removed.'
     end
   end
@@ -124,7 +123,7 @@ class ProjectsController < ApplicationController
     ::Projects::DestroyService.new(@project, current_user, {}).execute
     flash[:alert] = "Project '#{@project.name}' was deleted."
 
-    redirect_back_or_default(default: dashboard_projects_path, options: {})
+    redirect_to dashboard_projects_path
   rescue Projects::DestroyService::DestroyError => ex
     redirect_to edit_project_path(@project), alert: ex.message
   end
@@ -213,7 +212,8 @@ class ProjectsController < ApplicationController
     params.require(:project).permit(
       :name, :path, :description, :issues_tracker, :tag_list,
       :issues_enabled, :merge_requests_enabled, :snippets_enabled, :issues_tracker_id, :default_branch,
-      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar
+      :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar,
+      :builds_enabled
     )
   end
 
@@ -242,7 +242,7 @@ class ProjectsController < ApplicationController
     project.repository_exists? && !project.empty_repo?
   end
 
-  # Override get_id from ExtractsPath, which returns the branch and file path 
+  # Override get_id from ExtractsPath, which returns the branch and file path
   # for the blob/tree, which in this case is just the root of the default branch.
   def get_id
     project.repository.root_ref
diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb
index 9f9f9a92f1150439032f213787824428e8f07121..c72df73af46856e2f23184a602034ff403a611da 100644
--- a/app/controllers/snippets_controller.rb
+++ b/app/controllers/snippets_controller.rb
@@ -1,6 +1,9 @@
 class SnippetsController < ApplicationController
   before_action :snippet, only: [:show, :edit, :destroy, :update, :raw]
 
+  # Allow read snippet
+  before_action :authorize_read_snippet!, only: [:show, :raw]
+
   # Allow modify snippet
   before_action :authorize_update_snippet!, only: [:edit, :update]
 
@@ -79,10 +82,14 @@ class SnippetsController < ApplicationController
                      [Snippet::PUBLIC, Snippet::INTERNAL]).
                      find(params[:id])
                  else
-                   PersonalSnippet.are_public.find(params[:id])
+                   PersonalSnippet.find(params[:id])
                  end
   end
 
+  def authorize_read_snippet!
+    authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
+  end
+
   def authorize_update_snippet!
     return render_404 unless can?(current_user, :update_personal_snippet, @snippet)
   end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 1484356a7f49ec79472bc61ca17104afc4796a58..30cb869eb2a589cb9db99a988752f45495727b8b 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -3,14 +3,11 @@ class UsersController < ApplicationController
   before_action :set_user
 
   def show
-    @contributed_projects = contributed_projects.joined(@user).
-      reject(&:forked?)
+    @contributed_projects = contributed_projects.joined(@user).reject(&:forked?)
 
-    @projects = @user.personal_projects.
-      where(id: authorized_projects_ids).includes(:namespace)
+    @projects = PersonalProjectsFinder.new(@user).execute(current_user)
 
-    # Collect only groups common for both users
-    @groups = @user.groups & GroupsFinder.new.execute(current_user)
+    @groups = JoinedGroupsFinder.new(@user).execute(current_user)
 
     respond_to do |format|
       format.html
@@ -53,16 +50,8 @@ class UsersController < ApplicationController
     @user = User.find_by_username!(params[:username])
   end
 
-  def authorized_projects_ids
-    # Projects user can view
-    @authorized_projects_ids ||=
-      ProjectsFinder.new.execute(current_user).pluck(:id)
-  end
-
   def contributed_projects
-    @contributed_projects = Project.
-      where(id: authorized_projects_ids & @user.contributed_projects_ids).
-      includes(:namespace)
+    ContributedProjectsFinder.new(@user).execute(current_user)
   end
 
   def contributions_calendar
@@ -73,9 +62,13 @@ class UsersController < ApplicationController
   def load_events
     # Get user activity feed for projects common for both users
     @events = @user.recent_events.
-      where(project_id: authorized_projects_ids).
-      with_associations
+      merge(projects_for_current_user).
+      references(:project).
+      with_associations.
+      limit_recent(20, params[:offset])
+  end
 
-    @events = @events.limit(20).offset(params[:offset] || 0)
+  def projects_for_current_user
+    ProjectsFinder.new.execute(current_user)
   end
 end
diff --git a/app/finders/contributed_projects_finder.rb b/app/finders/contributed_projects_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0209649b017f672b4b5b65c68e637419dc1b379c
--- /dev/null
+++ b/app/finders/contributed_projects_finder.rb
@@ -0,0 +1,37 @@
+class ContributedProjectsFinder
+  def initialize(user)
+    @user = user
+  end
+
+  # Finds the projects "@user" contributed to, limited to either public projects
+  # or projects visible to the given user.
+  #
+  # current_user - When given the list of the projects is limited to those only
+  #                visible by this user.
+  #
+  # Returns an ActiveRecord::Relation.
+  def execute(current_user = nil)
+    if current_user
+      relation = projects_visible_to_user(current_user)
+    else
+      relation = public_projects
+    end
+
+    relation.includes(:namespace).order_id_desc
+  end
+
+  private
+
+  def projects_visible_to_user(current_user)
+    authorized = @user.contributed_projects.visible_to_user(current_user)
+
+    union = Gitlab::SQL::Union.
+      new([authorized.select(:id), public_projects.select(:id)])
+
+    Project.where("projects.id IN (#{union.to_sql})")
+  end
+
+  def public_projects
+    @user.contributed_projects.public_only
+  end
+end
diff --git a/app/finders/groups_finder.rb b/app/finders/groups_finder.rb
index b5f3176461c5a2894ae0f3ceb9e2b1512d667d05..91cb0f228f0a85abab9817c1c7dc8016568230bd 100644
--- a/app/finders/groups_finder.rb
+++ b/app/finders/groups_finder.rb
@@ -1,39 +1,44 @@
 class GroupsFinder
-  def execute(current_user, options = {})
-    all_groups(current_user)
+  # Finds the groups available to the given user.
+  #
+  # current_user - The user to find the groups for.
+  #
+  # Returns an ActiveRecord::Relation.
+  def execute(current_user = nil)
+    if current_user
+      relation = groups_visible_to_user(current_user)
+    else
+      relation = public_groups
+    end
+
+    relation.order_id_desc
   end
 
   private
 
-  def all_groups(current_user)
-    group_ids = if current_user
-                  if current_user.authorized_groups.any?
-                    # User has access to groups
-                    #
-                    # Return only:
-                    #   groups with public projects
-                    #   groups with internal projects
-                    #   groups with joined projects
-                    #
-                    Project.public_and_internal_only.pluck(:namespace_id) +
-                      current_user.authorized_groups.pluck(:id)
-                  else
-                    # User has no group membership
-                    #
-                    # Return only:
-                    #   groups with public projects
-                    #   groups with internal projects
-                    #
-                    Project.public_and_internal_only.pluck(:namespace_id)
-                  end
-                else
-                  # Not authenticated
-                  #
-                  # Return only:
-                  #   groups with public projects
-                  Project.public_only.pluck(:namespace_id)
-                end
-
-    Group.where("public IS TRUE OR id IN(?)", group_ids)
+  # This method returns the groups "current_user" can see.
+  def groups_visible_to_user(current_user)
+    base = groups_for_projects(public_and_internal_projects)
+
+    union = Gitlab::SQL::Union.
+      new([base.select(:id), current_user.authorized_groups.select(:id)])
+
+    Group.where("namespaces.id IN (#{union.to_sql})")
+  end
+
+  def public_groups
+    groups_for_projects(public_projects)
+  end
+
+  def groups_for_projects(projects)
+    Group.public_and_given_groups(projects.select(:namespace_id))
+  end
+
+  def public_projects
+    Project.unscoped.public_only
+  end
+
+  def public_and_internal_projects
+    Project.unscoped.public_and_internal_only
   end
 end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index c407dfc163a9bdd1ed50c98b033b44ba65106c4c..3d5e8b6fbe7e90c7f19e972f41ee784132cd3638 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -62,10 +62,10 @@ class IssuableFinder
 
     if project?
       @project = Project.find(params[:project_id])
-      
+
       unless Ability.abilities.allowed?(current_user, :read_project, @project)
         @project = nil
-      end 
+      end
     else
       @project = nil
     end
@@ -77,11 +77,11 @@ class IssuableFinder
     return @projects if defined?(@projects)
 
     if project?
-      project
+      @projects = project
     elsif current_user && params[:authorized_only].presence && !current_user_related?
-      current_user.authorized_projects
+      @projects = current_user.authorized_projects
     else
-      ProjectsFinder.new.execute(current_user)
+      @projects = ProjectsFinder.new.execute(current_user)
     end
   end
 
@@ -190,8 +190,10 @@ class IssuableFinder
 
   def by_project(items)
     items =
-      if projects
-        items.of_projects(projects).references(:project)
+      if project?
+        items.of_projects(projects).references_project
+      elsif projects
+        items.merge(projects.reorder(nil)).join_project
       else
         items.none
       end
@@ -206,7 +208,9 @@ class IssuableFinder
   end
 
   def sort(items)
-    items.sort(params[:sort])
+    # Ensure we always have an explicit sort order (instead of inheriting
+    # multiple orders when combining ActiveRecord::Relation objects).
+    params[:sort] ? items.sort(params[:sort]) : items.reorder(id: :desc)
   end
 
   def by_assignee(items)
diff --git a/app/finders/joined_groups_finder.rb b/app/finders/joined_groups_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e7523136fea6a8850480bb2457d8157151849858
--- /dev/null
+++ b/app/finders/joined_groups_finder.rb
@@ -0,0 +1,49 @@
+# Class for finding the groups a user is a member of.
+class JoinedGroupsFinder
+  def initialize(user = nil)
+    @user = user
+  end
+
+  # Finds the groups of the source user, optionally limited to those visible to
+  # the current user.
+  #
+  # current_user - If given the groups of "@user" will only include the groups
+  #                "current_user" can also see.
+  #
+  # Returns an ActiveRecord::Relation.
+  def execute(current_user = nil)
+    if current_user
+      relation = groups_visible_to_user(current_user)
+    else
+      relation = public_groups
+    end
+
+    relation.order_id_desc
+  end
+
+  private
+
+  # Returns the groups the user in "current_user" can see.
+  #
+  # This list includes all public/internal projects as well as the projects of
+  # "@user" that "current_user" also has access to.
+  def groups_visible_to_user(current_user)
+    base  = @user.authorized_groups.visible_to_user(current_user)
+    extra = public_and_internal_groups
+    union = Gitlab::SQL::Union.new([base.select(:id), extra.select(:id)])
+
+    Group.where("namespaces.id IN (#{union.to_sql})")
+  end
+
+  def public_groups
+    groups_for_projects(@user.authorized_projects.public_only)
+  end
+
+  def public_and_internal_groups
+    groups_for_projects(@user.authorized_projects.public_and_internal_only)
+  end
+
+  def groups_for_projects(projects)
+    @user.groups.public_and_given_groups(projects.select(:namespace_id))
+  end
+end
diff --git a/app/finders/milestones_finder.rb b/app/finders/milestones_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b704e878903b5f460d0d8b3a33955e3f2d051829
--- /dev/null
+++ b/app/finders/milestones_finder.rb
@@ -0,0 +1,12 @@
+class MilestonesFinder
+  def execute(projects, params)
+    milestones = Milestone.of_projects(projects)
+    milestones = milestones.order("due_date ASC")
+
+    case params[:state]
+    when 'closed' then milestones.closed
+    when 'all' then milestones
+    else milestones.active
+    end
+  end
+end
diff --git a/app/finders/notes_finder.rb b/app/finders/notes_finder.rb
index ab252821b52133c5d2294d4f8ee4e530b3bcfafa..fa4c635f55cfc589b78d01a38a120e0dbfae4cfc 100644
--- a/app/finders/notes_finder.rb
+++ b/app/finders/notes_finder.rb
@@ -12,9 +12,9 @@ class NotesFinder
       when "commit"
         project.notes.for_commit_id(target_id).not_inline
       when "issue"
-        project.issues.find(target_id).notes.inc_author
+        project.issues.find(target_id).notes.nonawards.inc_author
       when "merge_request"
-        project.merge_requests.find(target_id).mr_and_commit_notes.inc_author
+        project.merge_requests.find(target_id).mr_and_commit_notes.nonawards.inc_author
       when "snippet", "project_snippet"
         project.snippets.find(target_id).notes
       else
diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a61ffa229900574be7d1e0c8304b4c29baa7d780
--- /dev/null
+++ b/app/finders/personal_projects_finder.rb
@@ -0,0 +1,41 @@
+class PersonalProjectsFinder
+  def initialize(user)
+    @user = user
+  end
+
+  # Finds the projects belonging to the user in "@user", limited to either
+  # public projects or projects visible to the given user.
+  #
+  # current_user - When given the list of projects is limited to those only
+  #                visible by this user.
+  #
+  # Returns an ActiveRecord::Relation.
+  def execute(current_user = nil)
+    if current_user
+      relation = projects_visible_to_user(current_user)
+    else
+      relation = public_projects
+    end
+
+    relation.includes(:namespace).order_id_desc
+  end
+
+  private
+
+  def projects_visible_to_user(current_user)
+    authorized = @user.personal_projects.visible_to_user(current_user)
+
+    union = Gitlab::SQL::Union.
+      new([authorized.select(:id), public_and_internal_projects.select(:id)])
+
+    Project.where("projects.id IN (#{union.to_sql})")
+  end
+
+  def public_projects
+    @user.personal_projects.public_only
+  end
+
+  def public_and_internal_projects
+    @user.personal_projects.public_and_internal_only
+  end
+end
diff --git a/app/finders/projects_finder.rb b/app/finders/projects_finder.rb
index c81bb51583af82d3a012f21a025d31bd2444e933..3b4e0362e04baaf6203dc497173086297396b3b6 100644
--- a/app/finders/projects_finder.rb
+++ b/app/finders/projects_finder.rb
@@ -1,11 +1,39 @@
 class ProjectsFinder
-  def execute(current_user, options = {})
+  # Returns all projects, optionally including group projects a user has access
+  # to.
+  #
+  # ## Examples
+  #
+  # Retrieving all public projects:
+  #
+  #     ProjectsFinder.new.execute
+  #
+  # Retrieving all public/internal projects and those the given user has access
+  # to:
+  #
+  #     ProjectsFinder.new.execute(some_user)
+  #
+  # Retrieving all public/internal projects as well as the group's projects the
+  # user has access to:
+  #
+  #     ProjectsFinder.new.execute(some_user, group: some_group)
+  #
+  # Returns an ActiveRecord::Relation.
+  def execute(current_user = nil, options = {})
     group = options[:group]
 
     if group
-      group_projects(current_user, group)
+      segments = group_projects(current_user, group)
     else
-      all_projects(current_user)
+      segments = all_projects(current_user)
+    end
+
+    if segments.length > 1
+      union = Gitlab::SQL::Union.new(segments.map { |s| s.select(:id) })
+
+      Project.where("projects.id IN (#{union.to_sql})")
+    else
+      segments.first
     end
   end
 
@@ -13,77 +41,36 @@ class ProjectsFinder
 
   def group_projects(current_user, group)
     if current_user
-      if group.users.include?(current_user)
-        # User is group member
-        #
-        # Return ALL group projects
-        group.projects
-      else
-        projects_members = ProjectMember.in_projects(group.projects).
-          with_user(current_user)
-
-        if projects_members.any?
-          # User is a project member
-          #
-          # Return only:
-          #   public projects
-          #   internal projects
-          #   joined projects
-          #
-          group.projects.where(
-            "projects.id IN (?) OR projects.visibility_level IN (?)",
-            projects_members.pluck(:source_id),
-            Project.public_and_internal_levels
-          )
-        else
-          # User has no access to group or group projects
-          #
-          # Return only:
-          #   public projects
-          #   internal projects
-          #
-          group.projects.public_and_internal_only
-        end
-      end
+      [
+        group_projects_for_user(current_user, group),
+        group.projects.public_and_internal_only
+      ]
     else
-      # Not authenticated
-      #
-      # Return only:
-      #   public projects
-      group.projects.public_only
+      [group.projects.public_only]
     end
   end
 
   def all_projects(current_user)
     if current_user
-      if current_user.authorized_projects.any?
-        # User has access to private projects
-        #
-        # Return only:
-        #   public projects
-        #   internal projects
-        #   joined projects
-        #
-        Project.where(
-          "projects.id IN (?) OR projects.visibility_level IN (?)",
-          current_user.authorized_projects.pluck(:id),
-          Project.public_and_internal_levels
-        )
-      else
-        # User has no access to private projects
-        #
-        # Return only:
-        #   public projects
-        #   internal projects
-        #
-        Project.public_and_internal_only
-      end
+      [current_user.authorized_projects, public_and_internal_projects]
     else
-      # Not authenticated
-      #
-      # Return only:
-      #   public projects
-      Project.public_only
+      [Project.public_only]
     end
   end
+
+  def group_projects_for_user(current_user, group)
+    if group.users.include?(current_user)
+      group.projects
+    else
+      group.projects.visible_to_user(current_user)
+    end
+  end
+
+  def public_projects
+    Project.unscoped.public_only
+  end
+
+  def public_and_internal_projects
+    Project.unscoped.public_and_internal_only
+  end
 end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 8ecdeaf8e76ba8fe241838106c6ed9bc9509579c..3230ff1b0048d6d51f325c8a77e3d011c0f2f79b 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -68,7 +68,7 @@ module ApplicationHelper
     end
   end
 
-  def avatar_icon(user_or_email = nil, size = nil)
+  def avatar_icon(user_or_email = nil, size = nil, scale = 2)
     if user_or_email.is_a?(User)
       user = user_or_email
     else
@@ -78,12 +78,12 @@ module ApplicationHelper
     if user
       user.avatar_url(size) || default_avatar
     else
-      gravatar_icon(user_or_email, size)
+      gravatar_icon(user_or_email, size, scale)
     end
   end
 
-  def gravatar_icon(user_email = '', size = nil)
-    GravatarService.new.execute(user_email, size) ||
+  def gravatar_icon(user_email = '', size = nil, scale = 2)
+    GravatarService.new.execute(user_email, size, scale) ||
       default_avatar
   end
 
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 77d99140c43966073d36a45a37a0ddad2ade8b09..df5f5fae23cbcb8305768a185273f9548075b8d1 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -60,7 +60,7 @@ module BlobHelper
     if Gitlab::MarkupHelper.previewable?(filename)
       'Preview'
     else
-      'Preview changes'
+      'Preview Changes'
     end
   end
 
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..313b6dde9102795fda16d8e9eb4e1d322732a76d
--- /dev/null
+++ b/app/helpers/button_helper.rb
@@ -0,0 +1,58 @@
+module ButtonHelper
+  # Output a "Copy to Clipboard" button
+  #
+  # data - Data attributes passed to `content_tag`
+  #
+  # Examples:
+  #
+  #   # Define the clipboard's text
+  #   clipboard_button(clipboard_text: "Foo")
+  #   # => "<button class='...' data-clipboard-text='Foo'>...</button>"
+  #
+  #   # Define the target element
+  #   clipboard_button(clipboard_target: "#foo")
+  #   # => "<button class='...' data-clipboard-target='#foo'>...</button>"
+  #
+  # See http://clipboardjs.com/#usage
+  def clipboard_button(data = {})
+    content_tag :button,
+      icon('clipboard'),
+      class: 'btn btn-xs btn-clipboard',
+      data: data,
+      type: :button
+  end
+
+  def http_clone_button(project)
+    klass = 'btn js-protocol-switch'
+    klass << ' active'      if default_clone_protocol == 'http'
+    klass << ' has_tooltip' if current_user.try(:require_password?)
+
+    protocol = gitlab_config.protocol.upcase
+
+    content_tag :button, protocol,
+      class: klass,
+      data: {
+        clone: project.http_url_to_repo,
+        container: 'body',
+        html: 'true',
+        title: "Set a password on your account<br>to pull or push via #{protocol}"
+      },
+      type: :button
+  end
+
+  def ssh_clone_button(project)
+    klass = 'btn js-protocol-switch'
+    klass << ' active'      if default_clone_protocol == 'ssh'
+    klass << ' has_tooltip' if current_user.try(:require_ssh_key?)
+
+    content_tag :button, 'SSH',
+      class: klass,
+      data: {
+        clone: project.ssh_url_to_repo,
+        container: 'body',
+        html: 'true',
+        title: 'Add an SSH key to your profile<br>to pull or push via SSH.'
+      },
+      type: :button
+  end
+end
diff --git a/app/helpers/clipboard_helper.rb b/app/helpers/clipboard_helper.rb
deleted file mode 100644
index 3c1d7569fac3df89c38b464502382bd4763e521b..0000000000000000000000000000000000000000
--- a/app/helpers/clipboard_helper.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-module ClipboardHelper
-  def clipboard_button
-    content_tag :button,
-      icon('clipboard'),
-      class: 'btn btn-xs btn-clipboard js-clipboard-trigger',
-      type: :button
-  end
-end
diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb
index 9df20c9fce5d7b381d956dd65ba15805fa96d47f..590d20ac7b3495f37611935c015db301f2fdbce4 100644
--- a/app/helpers/commits_helper.rb
+++ b/app/helpers/commits_helper.rb
@@ -109,7 +109,7 @@ module CommitsHelper
         )
       elsif @path.present?
         return link_to(
-          "Browse Dir 禄",
+          "Browse Directory 禄",
           namespace_project_tree_path(project.namespace, project,
                                       tree_join(commit.id, @path)),
           class: "pull-right"
@@ -117,7 +117,7 @@ module CommitsHelper
       end
     end
     link_to(
-      "Browse Code 禄",
+      "Browse Files 禄",
       namespace_project_tree_path(project.namespace, project, commit),
       class: "pull-right"
     )
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index b889fb2897349f20ec7b80cce26843495f08d252..24134310fc5d084b4cfa15f1e1c6009b9c51f476 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -1,4 +1,8 @@
 module DiffHelper
+  def diff_view
+    params[:view] == 'parallel' ? 'parallel' : 'inline'
+  end
+
   def allowed_diff_size
     if diff_hard_limit_enabled?
       Commit::DIFF_HARD_LIMIT_FILES
@@ -132,33 +136,19 @@ module DiffHelper
   end
 
   def inline_diff_btn
-    params_copy = params.dup
-    params_copy[:view] = 'inline'
-    # Always use HTML to handle case where JSON diff rendered this button
-    params_copy.delete(:format)
-
-    link_to url_for(params_copy), id: "inline-diff-btn", class: (params[:view] != 'parallel' ? 'btn active' : 'btn') do
-      'Inline'
-    end
+    diff_btn('Inline', 'inline', diff_view == 'inline')
   end
 
   def parallel_diff_btn
-    params_copy = params.dup
-    params_copy[:view] = 'parallel'
-    # Always use HTML to handle case where JSON diff rendered this button
-    params_copy.delete(:format)
-
-    link_to url_for(params_copy), id: "parallel-diff-btn", class: (params[:view] == 'parallel' ? 'btn active' : 'btn') do
-      'Side-by-side'
-    end
+    diff_btn('Side-by-side', 'parallel', diff_view == 'parallel')
   end
 
   def submodule_link(blob, ref, repository = @repository)
     tree, commit = submodule_links(blob, ref, repository)
     commit_id = if commit.nil?
-                  blob.id[0..10]
+                  Commit.truncate_sha(blob.id)
                 else
-                  link_to "#{blob.id[0..10]}", commit
+                  link_to Commit.truncate_sha(blob.id), commit
                 end
 
     [
@@ -171,7 +161,7 @@ module DiffHelper
   def commit_for_diff(diff)
     if diff.deleted_file
       first_commit = @first_commit || @commit
-      first_commit.parent
+      first_commit.parent || @first_commit
     else
       @commit
     end
@@ -187,4 +177,18 @@ module DiffHelper
   def editable_diff?(diff)
     !diff.deleted_file && @merge_request && @merge_request.source_project
   end
+
+  private
+
+  def diff_btn(title, name, selected)
+    params_copy = params.dup
+    params_copy[:view] = name
+
+    # Always use HTML to handle case where JSON diff rendered this button
+    params_copy.delete(:format)
+
+    link_to url_for(params_copy), id: "#{name}-diff-btn", class: (selected ? 'btn active' : 'btn') do
+      title
+    end
+  end
 end
diff --git a/app/helpers/emails_helper.rb b/app/helpers/emails_helper.rb
index 45788ba95ac889eca40e85d23ff15549136a1ae8..41b5bd7be9060b5c7b658a92680a7e9bc9e2ffc8 100644
--- a/app/helpers/emails_helper.rb
+++ b/app/helpers/emails_helper.rb
@@ -28,6 +28,8 @@ module EmailsHelper
         return "View #{action.humanize.singularize}"
       end
     end
+
+    nil
   end
 
   def color_email_diff(diffcontent)
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index 6f69c2a9f329a4a19e84c5f9b14e388f312ba232..dde83ff36b50b2924682ce306b49c24055af5d58 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -108,19 +108,23 @@ module EventsHelper
         end
       end
     elsif event.push?
-      if event.push_with_commits? && event.md_ref?
-        if event.commits_count > 1
-          namespace_project_compare_url(event.project.namespace, event.project,
-                                        from: event.commit_from, to:
-                                        event.commit_to)
-        else
-          namespace_project_commit_url(event.project.namespace, event.project,
-                                       id: event.commit_to)
-        end
+      push_event_feed_url(event)
+    end
+  end
+
+  def push_event_feed_url(event)
+    if event.push_with_commits? && event.md_ref?
+      if event.commits_count > 1
+        namespace_project_compare_url(event.project.namespace, event.project,
+                                      from: event.commit_from, to:
+                                      event.commit_to)
       else
-        namespace_project_commits_url(event.project.namespace, event.project,
-                                      event.ref_name)
+        namespace_project_commit_url(event.project.namespace, event.project,
+                                     id: event.commit_to)
       end
+    else
+      namespace_project_commits_url(event.project.namespace, event.project,
+                                    event.ref_name)
     end
   end
 
@@ -198,7 +202,7 @@ module EventsHelper
         xml.link    href: event_link
         xml.title   truncate(event_title, length: 80)
         xml.updated event.created_at.xmlschema
-        xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(event.author_email)
+        xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(event.author_email))
         xml.author do |author|
           xml.name event.author_name
           xml.email event.author_email
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 658134821202f227b264514bbb58161128949021..98c6d9d5d2ece0660c58b92194537cda308a1d74 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -46,39 +46,13 @@ module GitlabMarkdownHelper
   end
 
   def markdown(text, context = {})
-    return "" unless text.present?
-
-    context.reverse_merge!(
-      path:         @path,
-      pipeline:     :default,
-      project:      @project,
-      project_wiki: @project_wiki,
-      ref:          @ref
-    )
-
-    user = current_user if defined?(current_user)
-
-    html = Gitlab::Markdown.render(text, context)
-    Gitlab::Markdown.post_process(html, pipeline: context[:pipeline], project: @project, user: user)
+    process_markdown(text, context)
   end
 
   # TODO (rspeicher): Remove all usages of this helper and just call `markdown`
   # with a custom pipeline depending on the content being rendered
   def gfm(text, options = {})
-    return "" unless text.present?
-
-    options.reverse_merge!(
-      path:         @path,
-      pipeline:     :default,
-      project:      @project,
-      project_wiki: @project_wiki,
-      ref:          @ref
-    )
-
-    user = current_user if defined?(current_user)
-
-    html = Gitlab::Markdown.gfm(text, options)
-    Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user)
+    process_markdown(text, options, :gfm)
   end
 
   def asciidoc(text)
@@ -204,4 +178,26 @@ module GitlabMarkdownHelper
       ''
     end
   end
+
+  def process_markdown(text, options, method = :markdown)
+    return "" unless text.present?
+
+    options.reverse_merge!(
+      path:         @path,
+      pipeline:     :default,
+      project:      @project,
+      project_wiki: @project_wiki,
+      ref:          @ref
+    )
+
+    user = current_user if defined?(current_user)
+
+    html = if method == :gfm
+             Gitlab::Markdown.gfm(text, options)
+           else
+             Gitlab::Markdown.render(text, options)
+           end
+
+    Gitlab::Markdown.post_process(html, pipeline: options[:pipeline], project: @project, user: user)
+  end
 end
diff --git a/app/helpers/icons_helper.rb b/app/helpers/icons_helper.rb
index 1cf5b96481a952ba4d35108bd4c5ac8a2b66f0a5..5724d3aabecd861796d0d81a164822f79cddd8af 100644
--- a/app/helpers/icons_helper.rb
+++ b/app/helpers/icons_helper.rb
@@ -27,16 +27,20 @@ module IconsHelper
     end
   end
 
-  def public_icon
-    icon('globe fw')
-  end
-
-  def internal_icon
-    icon('shield fw')
-  end
+  def visibility_level_icon(level, fw: true)
+    name =
+      case level
+      when Gitlab::VisibilityLevel::PRIVATE
+        'lock'
+      when Gitlab::VisibilityLevel::INTERNAL
+        'shield'
+      else # Gitlab::VisibilityLevel::PUBLIC
+        'globe'
+      end
+      
+    name << " fw" if fw
 
-  def private_icon
-    icon('lock fw')
+    icon(name)
   end
 
   def file_type_icon_class(type, mode, name)
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index fda18e7b316d7c130338ac6e85a47eaf0d17d3bc..da2e0db0653d01e9076605a78a9b245c6d8a8a13 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -44,9 +44,10 @@ module IssuesHelper
   end
 
   def bulk_update_milestone_options
-    options_for_select([['None (backlog)', -1]]) +
-        options_from_collection_for_select(project_active_milestones, 'id',
-                                           'title', params[:milestone_id])
+    milestones = project_active_milestones.to_a
+    milestones.unshift(Milestone::None)
+
+    options_from_collection_for_select(milestones, 'id', 'title', params[:milestone_id])
   end
 
   def milestone_options(object)
@@ -74,7 +75,7 @@ module IssuesHelper
                                                     issue.project, issue)
       xml.title   truncate(issue.title, length: 80)
       xml.updated issue.created_at.strftime("%Y-%m-%dT%H:%M:%SZ")
-      xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(issue.author_email)
+      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(issue.author_email))
       xml.author do |author|
         xml.name issue.author_name
         xml.email issue.author_email
@@ -87,6 +88,33 @@ module IssuesHelper
     merge_requests.map(&:to_reference).to_sentence(last_word_connector: ', or ')
   end
 
+  def url_to_emoji(name)
+    emoji_path = ::AwardEmoji.path_to_emoji_image(name)
+    url_to_image(emoji_path)
+  rescue StandardError
+    ""
+  end
+
+  def emoji_author_list(notes, current_user)
+    list = notes.map do |note|
+             note.author == current_user ? "me" : note.author.name
+           end
+
+    list.join(", ")
+  end
+
+  def emoji_list
+    ::AwardEmoji::EMOJI_LIST
+  end
+
+  def note_active_class(notes, current_user)
+    if current_user && notes.pluck(:author_id).include?(current_user.id)
+      "active"
+    else
+      ""
+    end
+  end
+
   # Required for Gitlab::Markdown::IssueReferenceFilter
   module_function :url_for_issue
 end
diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb
index ee04ace35d0933e74386739f275c625d4a6ecedb..795fb439f256ba5bba14acda80f938fbd61b91c7 100644
--- a/app/helpers/labels_helper.rb
+++ b/app/helpers/labels_helper.rb
@@ -100,7 +100,7 @@ module LabelsHelper
         Label.where(project_id: @projects)
       end
 
-    grouped_labels = Labels::GroupService.new(labels).execute
+    grouped_labels = GlobalLabel.build_collection(labels)
     grouped_labels.unshift(Label::None)
     grouped_labels.unshift(Label::Any)
 
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index 728d877ace226e73b574c191fec62875a3f22415..b804d4f4e3b684c6e6bb901b944899f01fbdb20e 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -8,14 +8,6 @@ module MergeRequestsHelper
     )
   end
 
-  def new_mr_path_for_fork_from_push_event(event)
-    new_namespace_project_merge_request_path(
-      event.project.namespace,
-      event.project,
-      new_mr_from_push_event(event, event.project.forked_from_project)
-    )
-  end
-
   def new_mr_from_push_event(event, target_project)
     {
       merge_request: {
diff --git a/app/helpers/milestones_helper.rb b/app/helpers/milestones_helper.rb
index 37a5b58cce8be8efd3ad934662b239b529cc02d4..ad43892b6399cc14e1312afd52f34cbefa1654e9 100644
--- a/app/helpers/milestones_helper.rb
+++ b/app/helpers/milestones_helper.rb
@@ -28,7 +28,7 @@ module MilestonesHelper
         Milestone.where(project_id: @projects)
       end.active
 
-    grouped_milestones = Milestones::GroupService.new(milestones).execute
+    grouped_milestones = GlobalMilestone.build_collection(milestones)
     grouped_milestones.unshift(Milestone::None)
     grouped_milestones.unshift(Milestone::Any)
 
diff --git a/app/helpers/namespaces_helper.rb b/app/helpers/namespaces_helper.rb
index b3132a1f3ba1e43e289a196793503182a2b5e07e..faba418c4db259614926d356d47dcfca51102b7e 100644
--- a/app/helpers/namespaces_helper.rb
+++ b/app/helpers/namespaces_helper.rb
@@ -1,10 +1,10 @@
 module NamespacesHelper
-  def namespaces_options(selected = :current_user, scope = :default)
+  def namespaces_options(selected = :current_user, display_path: false)
     groups = current_user.owned_groups + current_user.masters_groups
     users = [current_user.namespace]
 
-    group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [g.human_name, g.id]} ]
-    users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [u.human_name, u.id]} ]
+    group_opts = ["Groups", groups.sort_by(&:human_name).map {|g| [display_path ? g.path : g.human_name, g.id]} ]
+    users_opts = [ "Users", users.sort_by(&:human_name).map {|u| [display_path ? u.path : u.human_name, u.id]} ]
 
     options = []
     options << group_opts
@@ -17,15 +17,6 @@ module NamespacesHelper
     grouped_options_for_select(options, selected)
   end
 
-  def namespace_select_tag(id, opts = {})
-    css_class = "ajax-namespace-select "
-    css_class << "multiselect " if opts[:multiple]
-    css_class << (opts[:class] || '')
-    value = opts[:selected] || ''
-
-    hidden_field_tag(id, value, class: css_class)
-  end
-
   def namespace_icon(namespace, size = 40)
     if namespace.kind_of?(Group)
       group_icon(namespace)
diff --git a/app/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb
index cf11f8e5320651c0afb767ddd287d6e11c59c673..499c655d2bf21991eb9955aff39ca948665cee64 100644
--- a/app/helpers/notifications_helper.rb
+++ b/app/helpers/notifications_helper.rb
@@ -16,40 +16,28 @@ module NotificationsHelper
   def notification_list_item(notification_level, user_membership)
     case notification_level
     when Notification::N_DISABLED
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_DISABLED)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_DISABLED } do
-          icon('microphone-slash fw', text: 'Disabled')
-        end
-      end
+      update_notification_link(Notification::N_DISABLED, user_membership, 'Disabled', 'microphone-slash')
     when Notification::N_PARTICIPATING
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_PARTICIPATING)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_PARTICIPATING } do
-          icon('volume-up fw', text: 'Participate')
-        end
-      end
+      update_notification_link(Notification::N_PARTICIPATING, user_membership, 'Participate', 'volume-up')
     when Notification::N_WATCH
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_WATCH)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_WATCH } do
-          icon('eye fw', text: 'Watch')
-        end
-      end
+      update_notification_link(Notification::N_WATCH, user_membership, 'Watch', 'eye')
     when Notification::N_MENTION
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_MENTION)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_MENTION }  do
-          icon('at fw', text: 'On mention')
-        end
-      end
+      update_notification_link(Notification::N_MENTION, user_membership, 'On mention', 'at')
     when Notification::N_GLOBAL
-      content_tag(:li, class: active_level_for(user_membership, Notification::N_GLOBAL)) do
-        link_to '#', class: 'update-notification', data: { notification_level: Notification::N_GLOBAL } do
-          icon('globe fw', text: 'Global')
-        end
-      end
+      update_notification_link(Notification::N_GLOBAL, user_membership, 'Global', 'globe')
     else
       # do nothing
     end
   end
 
+  def update_notification_link(notification_level, user_membership, title, icon)
+    content_tag(:li, class: active_level_for(user_membership, notification_level)) do
+      link_to '#', class: 'update-notification', data: { notification_level: notification_level } do
+        icon("#{icon} fw", text: title)
+      end
+    end
+  end
+
   def notification_label(user_membership)
     Notification.new(user_membership).to_s
   end
diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb
index 5301c2ccf7688a0b293f28fe27b3577e23cde479..48729e5260e608e45c3d96ba455c64054b90ff8e 100644
--- a/app/helpers/projects_helper.rb
+++ b/app/helpers/projects_helper.rb
@@ -21,7 +21,7 @@ module ProjectsHelper
   end
 
   def link_to_member(project, author, opts = {})
-    default_opts = { avatar: true, name: true, size: 16, author_class: 'author' }
+    default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" }
     opts = default_opts.merge(opts)
 
     return "(deleted)" unless author
@@ -39,7 +39,8 @@ module ProjectsHelper
     if opts[:name]
       link_to(author_html, user_path(author), class: "author_link").html_safe
     else
-      link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => sanitize(author.name) } ).html_safe
+      title = opts[:title].sub(":name", sanitize(author.name))
+      link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { :'original-title' => title, container: 'body' } ).html_safe
     end
   end
 
@@ -117,7 +118,7 @@ module ProjectsHelper
       nav_tabs << :merge_requests
     end
 
-    if project.gitlab_ci? && can?(current_user, :read_build, project)
+    if project.builds_enabled? && can?(current_user, :read_build, project)
       nav_tabs << :builds
     end
 
@@ -173,8 +174,7 @@ module ProjectsHelper
     'unknown'
   end
 
-  def default_url_to_repo(project = nil)
-    project = project || @project
+  def default_url_to_repo(project = @project)
     current_user ? project.url_to_repo : project.http_url_to_repo
   end
 
@@ -253,14 +253,6 @@ module ProjectsHelper
     filename_path(project, :version)
   end
 
-  def hidden_pass_url(original_url)
-    result = URI(original_url)
-    result.password = '*****' unless result.password.nil?
-    result
-  rescue
-    original_url
-  end
-
   def project_wiki_path_with_version(proj, page, version, is_newest)
     url_params = is_newest ? {} : { version_id: version }
     namespace_project_wiki_path(proj.namespace, proj, page, url_params)
diff --git a/app/helpers/selects_helper.rb b/app/helpers/selects_helper.rb
index 12fce8db7013c34b70fc7626144caaee363c4ffc..7e175d0de8a7cfa7b3c3f195c78f983abbc93dcf 100644
--- a/app/helpers/selects_helper.rb
+++ b/app/helpers/selects_helper.rb
@@ -15,12 +15,14 @@ module SelectsHelper
 
     html = {
       class: css_class,
-      'data-placeholder' => placeholder,
-      'data-null-user' => null_user,
-      'data-any-user' => any_user,
-      'data-email-user' => email_user,
-      'data-first-user' => first_user,
-      'data-current-user' => current_user
+      data: {
+        placeholder: placeholder,
+        null_user: null_user,
+        any_user: any_user,
+        email_user: email_user,
+        first_user: first_user,
+        current_user: current_user
+      }
     }
 
     unless opts[:scope] == :all
@@ -35,8 +37,20 @@ module SelectsHelper
   end
 
   def groups_select_tag(id, opts = {})
-    css_class = "ajax-groups-select "
-    css_class << "multiselect " if opts[:multiple]
+    opts[:class] ||= ''
+    opts[:class] << ' ajax-groups-select'
+    select2_tag(id, opts)
+  end
+
+  def namespace_select_tag(id, opts = {})
+    opts[:class] ||= ''
+    opts[:class] << ' ajax-namespace-select'
+    select2_tag(id, opts)
+  end
+
+  def select2_tag(id, opts = {})
+    css_class = ''
+    css_class << 'multiselect ' if opts[:multiple]
     css_class << (opts[:class] || '')
     value = opts[:selected] || ''
 
diff --git a/app/helpers/visibility_level_helper.rb b/app/helpers/visibility_level_helper.rb
index b52cd23aba22e378ad114ce8e8ed690d7f4d194f..72c65030f944755de07195516372187d270414c6 100644
--- a/app/helpers/visibility_level_helper.rb
+++ b/app/helpers/visibility_level_helper.rb
@@ -25,48 +25,24 @@ module VisibilityLevelHelper
   end
 
   def project_visibility_level_description(level)
-    capture_haml do
-      haml_tag :span do
-        case level
-        when Gitlab::VisibilityLevel::PRIVATE
-          haml_concat "Project access must be granted explicitly for each user."
-        when Gitlab::VisibilityLevel::INTERNAL
-          haml_concat "The project can be cloned by"
-          haml_concat "any logged in user."
-        when Gitlab::VisibilityLevel::PUBLIC
-          haml_concat "The project can be cloned"
-          haml_concat "without any"
-          haml_concat "authentication."
-        end
-      end
+    case level
+    when Gitlab::VisibilityLevel::PRIVATE
+      "Project access must be granted explicitly for each user."
+    when Gitlab::VisibilityLevel::INTERNAL
+      "The project can be cloned by any logged in user."
+    when Gitlab::VisibilityLevel::PUBLIC
+      "The project can be cloned without any authentication."
     end
   end
 
   def snippet_visibility_level_description(level)
-    capture_haml do
-      haml_tag :span do
-        case level
-        when Gitlab::VisibilityLevel::PRIVATE
-          haml_concat "The snippet is visible only for me."
-        when Gitlab::VisibilityLevel::INTERNAL
-          haml_concat "The snippet is visible for any logged in user."
-        when Gitlab::VisibilityLevel::PUBLIC
-          haml_concat "The snippet can be accessed"
-          haml_concat "without any"
-          haml_concat "authentication."
-        end
-      end
-    end
-  end
-
-  def visibility_level_icon(level)
     case level
     when Gitlab::VisibilityLevel::PRIVATE
-      private_icon
+      "The snippet is visible only for me."
     when Gitlab::VisibilityLevel::INTERNAL
-      internal_icon
+      "The snippet is visible for any logged in user."
     when Gitlab::VisibilityLevel::PUBLIC
-      public_icon
+      "The snippet can be accessed without any authentication."
     end
   end
 
diff --git a/app/mailers/base_mailer.rb b/app/mailers/base_mailer.rb
index aedb0889185cf27e2393d0fb6abb9e2d34bc5a03..8b83bbd93b74fc42d04bf4470f0d4d39586486e0 100644
--- a/app/mailers/base_mailer.rb
+++ b/app/mailers/base_mailer.rb
@@ -8,10 +8,6 @@ class BaseMailer < ActionMailer::Base
   default from:     Proc.new { default_sender_address.format }
   default reply_to: Proc.new { default_reply_to_address.format }
 
-  def self.delay
-    delay_for(2.seconds)
-  end
-
   def can?
     Ability.abilities.allowed?(current_user, action, subject)
   end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index 2c035fbb70bdce3d02c8a6649f07a5e863ce3385..abdeefed5ef8b05a3d1b529a1e31771adf704fb1 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -1,53 +1,49 @@
 module Emails
   module Issues
     def new_issue_email(recipient_id, issue_id)
-      @issue = Issue.find(issue_id)
-      @project = @issue.project
-      @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
-      mail_new_thread(@issue,
-                      from: sender(@issue.author_id),
-                      to: recipient(recipient_id),
-                      subject: subject("#{@issue.title} (##{@issue.iid})"))
-
-      SentNotification.record(@issue, recipient_id, reply_key)
+      issue_mail_with_notification(issue_id, recipient_id) do
+        mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
+      end
     end
 
     def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
-      @issue = Issue.find(issue_id)
-      @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
-      @project = @issue.project
-      @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
-      mail_answer_thread(@issue,
-                         from: sender(updated_by_user_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@issue.title} (##{@issue.iid})"))
-
-      SentNotification.record(@issue, recipient_id, reply_key)
+      issue_mail_with_notification(issue_id, recipient_id) do
+        @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
+        mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+      end
     end
 
     def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
-      @issue = Issue.find issue_id
-      @project = @issue.project
-      @updated_by = User.find updated_by_user_id
-      @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
-      mail_answer_thread(@issue,
-                         from: sender(updated_by_user_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@issue.title} (##{@issue.iid})"))
-
-      SentNotification.record(@issue, recipient_id, reply_key)
+      issue_mail_with_notification(issue_id, recipient_id) do
+        @updated_by = User.find updated_by_user_id
+        mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+      end
     end
 
     def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
-      @issue = Issue.find issue_id
-      @issue_status = status
+      issue_mail_with_notification(issue_id, recipient_id) do
+        @issue_status = status
+        @updated_by = User.find updated_by_user_id
+        mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
+      end
+    end
+
+    private
+
+    def issue_thread_options(sender_id, recipient_id)
+      {
+        from: sender(sender_id),
+        to: recipient(recipient_id),
+        subject: subject("#{@issue.title} (##{@issue.iid})")
+      }
+    end
+
+    def issue_mail_with_notification(issue_id, recipient_id)
+      @issue = Issue.find(issue_id)
       @project = @issue.project
-      @updated_by = User.find updated_by_user_id
       @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
-      mail_answer_thread(@issue,
-                         from: sender(updated_by_user_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@issue.title} (##{@issue.iid})"))
+
+      yield
 
       SentNotification.record(@issue, recipient_id, reply_key)
     end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index 87ba94a583d3671dbf26b77ec7c2cc4ddbf833f6..65f37e92677c5480dd671ee6504cfe33d8636e33 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -1,49 +1,54 @@
 module Emails
   module Notes
     def note_commit_email(recipient_id, note_id)
-      @note = Note.find(note_id)
-      @commit = @note.noteable
-      @project = @note.project
-      @target_url = namespace_project_commit_url(@project.namespace, @project,
-                                                 @commit, anchor:
-                                                 "note_#{@note.id}")
-      mail_answer_thread(@commit,
-                         from: sender(@note.author_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@commit.title} (#{@commit.short_id})"))
-
-      SentNotification.record_note(@note, recipient_id, reply_key)
+      note_mail_with_notification(note_id, recipient_id) do
+        @commit = @note.noteable
+        @target_url = namespace_project_commit_url(*note_target_url_options)
+
+        mail_answer_thread(@commit,
+                           from: sender(@note.author_id),
+                           to: recipient(recipient_id),
+                           subject: subject("#{@commit.title} (#{@commit.short_id})"))
+      end
     end
 
     def note_issue_email(recipient_id, note_id)
-      @note = Note.find(note_id)
-      @issue = @note.noteable
-      @project = @note.project
-      @target_url = namespace_project_issue_url(@project.namespace, @project,
-                                                @issue, anchor:
-                                                "note_#{@note.id}")
-      mail_answer_thread(@issue,
-                         from: sender(@note.author_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@issue.title} (##{@issue.iid})"))
-
-      SentNotification.record_note(@note, recipient_id, reply_key)
+      note_mail_with_notification(note_id, recipient_id) do
+        @issue = @note.noteable
+        @target_url = namespace_project_issue_url(*note_target_url_options)
+        mail_answer_thread(@issue, note_thread_options(recipient_id))
+      end
     end
 
     def note_merge_request_email(recipient_id, note_id)
+      note_mail_with_notification(note_id, recipient_id) do
+        @merge_request = @note.noteable
+        @target_url = namespace_project_merge_request_url(*note_target_url_options)
+        mail_answer_thread(@merge_request, note_thread_options(recipient_id))
+      end
+    end
+
+    private
+
+    def note_target_url_options
+      [@project.namespace, @project, @note.noteable, anchor: "note_#{@note.id}"]
+    end
+
+    def note_thread_options(recipient_id)
+      {
+        from: sender(@note.author_id),
+        to: recipient(recipient_id),
+        subject: subject("#{@note.noteable.title} (##{@note.noteable.iid})")
+      }
+    end
+
+    def note_mail_with_notification(note_id, recipient_id)
       @note = Note.find(note_id)
-      @merge_request = @note.noteable
       @project = @note.project
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request, anchor:
-                                                        "note_#{@note.id}")
-      mail_answer_thread(@merge_request,
-                         from: sender(@note.author_id),
-                         to: recipient(recipient_id),
-                         subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
-
-      SentNotification.record_note(@note, recipient_id, reply_key)
+
+      yield
+
+      SentNotification.record(@note, recipient_id, reply_key)
     end
   end
 end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 5ae28d5133e63296f885e26c6d72ed3c038ebebc..07f3a56ec7abd8c8d245d33c398e2c32475efc54 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -1,8 +1,8 @@
 class Ability
   class << self
     def allowed(user, subject)
-      return not_auth_abilities(user, subject) if user.nil?
-      return [] unless user.kind_of?(User)
+      return anonymous_abilities(user, subject) if user.nil?
+      return [] unless user.is_a?(User)
       return [] if user.blocked?
 
       case subject.class.name
@@ -15,19 +15,30 @@ class Ability
       when "Group" then group_abilities(user, subject)
       when "Namespace" then namespace_abilities(user, subject)
       when "GroupMember" then group_member_abilities(user, subject)
+      when "ProjectMember" then project_member_abilities(user, subject)
       else []
       end.concat(global_abilities(user))
     end
 
-    # List of possible abilities
-    # for non-authenticated user
-    def not_auth_abilities(user, subject)
-      project = if subject.kind_of?(Project)
+    # List of possible abilities for anonymous user
+    def anonymous_abilities(user, subject)
+      case true
+      when subject.is_a?(PersonalSnippet)
+        anonymous_personal_snippet_abilities(subject)
+      when subject.is_a?(Project) || subject.respond_to?(:project)
+        anonymous_project_abilities(subject)
+      when subject.is_a?(Group) || subject.respond_to?(:group)
+        anonymous_group_abilities(subject)
+      else
+        []
+      end
+    end
+
+    def anonymous_project_abilities(subject)
+      project = if subject.is_a?(Project)
                   subject
-                elsif subject.respond_to?(:project)
-                  subject.project
                 else
-                  nil
+                  subject.project
                 end
 
       if project && project.public?
@@ -47,19 +58,29 @@ class Ability
 
         rules - project_disabled_features_rules(project)
       else
-        group = if subject.kind_of?(Group)
-                  subject
-                elsif subject.respond_to?(:group)
-                  subject.group
-                else
-                  nil
-                end
+        []
+      end
+    end
 
-        if group && group.public_profile?
-          [:read_group]
-        else
-          []
-        end
+    def anonymous_group_abilities(subject)
+      group = if subject.is_a?(Group)
+                subject
+              else
+                subject.group
+              end
+
+      if group && group.public_profile?
+        [:read_group]
+      else
+        []
+      end
+    end
+
+    def anonymous_personal_snippet_abilities(snippet)
+      if snippet.public?
+        [:read_personal_snippet]
+      else
+        []
       end
     end
 
@@ -231,18 +252,19 @@ class Ability
 
       # Only group masters and group owners can create new projects in group
       if group.has_master?(user) || group.has_owner?(user) || user.admin?
-        rules.push(*[
+        rules += [
           :create_projects,
-        ])
+          :admin_milestones
+        ]
       end
 
       # Only group owner and administrators can admin group
       if group.has_owner?(user) || user.admin?
-        rules.push(*[
+        rules += [
           :admin_group,
           :admin_namespace,
           :admin_group_member
-        ])
+        ]
       end
 
       rules.flatten
@@ -253,16 +275,15 @@ class Ability
 
       # Only namespace owner and administrators can admin it
       if namespace.owner == user || user.admin?
-        rules.push(*[
+        rules += [
           :create_projects,
           :admin_namespace
-        ])
+        ]
       end
 
       rules.flatten
     end
 
-
     [:issue, :merge_request].each do |name|
       define_method "#{name}_abilities" do |user, subject|
         rules = []
@@ -279,7 +300,7 @@ class Ability
       end
     end
 
-    [:note, :project_snippet, :personal_snippet].each do |name|
+    [:note, :project_snippet].each do |name|
       define_method "#{name}_abilities" do |user, subject|
         rules = []
 
@@ -299,19 +320,61 @@ class Ability
       end
     end
 
+    def personal_snippet_abilities(user, snippet)
+      rules = []
+
+      if snippet.author == user
+        rules += [
+          :read_personal_snippet,
+          :update_personal_snippet,
+          :admin_personal_snippet
+        ]
+      end
+
+      if snippet.public? || snippet.internal?
+        rules << :read_personal_snippet 
+      end
+
+      rules
+    end
+
     def group_member_abilities(user, subject)
       rules = []
       target_user = subject.user
       group = subject.group
-      can_manage = group_abilities(user, group).include?(:admin_group_member)
 
-      if can_manage && (user != target_user)
-        rules << :update_group_member
-        rules << :destroy_group_member
+      unless group.last_owner?(target_user)
+        can_manage = group_abilities(user, group).include?(:admin_group_member)
+
+        if can_manage && user != target_user
+          rules << :update_group_member
+          rules << :destroy_group_member
+        end
+
+        if user == target_user
+          rules << :destroy_group_member
+        end
       end
 
-      if !group.last_owner?(user) && (can_manage || (user == target_user))
-        rules << :destroy_group_member
+      rules
+    end
+
+    def project_member_abilities(user, subject)
+      rules = []
+      target_user = subject.user
+      project = subject.project
+
+      unless target_user == project.owner
+        can_manage = project_abilities(user, project).include?(:admin_project_member)
+
+        if can_manage && user != target_user
+          rules << :update_project_member
+          rules << :destroy_project_member
+        end
+
+        if user == target_user
+          rules << :destroy_project_member
+        end
       end
 
       rules
@@ -319,10 +382,10 @@ class Ability
 
     def abilities
       @abilities ||= begin
-                       abilities = Six.new
-                       abilities << self
-                       abilities
-                     end
+        abilities = Six.new
+        abilities << self
+        abilities
+      end
     end
 
     private
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index fa7cf2464ad7b6771faaa1f6d9fbb2fba196d5bd..3df8135acf150d08e64339fe7a78c78a831b0864 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -23,6 +23,10 @@
 #  after_sign_out_path          :string(255)
 #  session_expire_delay         :integer          default(10080), not null
 #  import_sources               :text
+#  help_page_text               :text
+#  admin_notification_email     :string(255)
+#  shared_runners_enabled       :boolean          default(TRUE), not null
+#  max_artifacts_size           :integer          default(100), not null
 #
 
 class ApplicationSetting < ActiveRecord::Base
@@ -68,8 +72,22 @@ class ApplicationSetting < ActiveRecord::Base
     end
   end
 
+  after_commit do
+    Rails.cache.write(cache_key, self)
+  end
+
   def self.current
-    ApplicationSetting.last
+    Rails.cache.fetch(cache_key) do
+      ApplicationSetting.last
+    end
+  end
+
+  def self.expire
+    Rails.cache.delete(cache_key)
+  end
+
+  def self.cache_key
+    'application_setting.last'
   end
 
   def self.create_from_defaults
@@ -89,7 +107,7 @@ class ApplicationSetting < ActiveRecord::Base
       restricted_signup_domains: Settings.gitlab['restricted_signup_domains'],
       import_sources: ['github','bitbucket','gitlab','gitorious','google_code','fogbugz','git'],
       shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
-      max_artifacts_size: Settings.gitlab_ci['max_artifacts_size'],
+      max_artifacts_size: Settings.artifacts['max_size'],
     )
   end
 
diff --git a/app/models/ci/application_setting.rb b/app/models/ci/application_setting.rb
index 0cf496f7d81490d78b47b23d0cb514ce4e5b5c2d..4e512d290ee07633145164b819a2eff0671644dc 100644
--- a/app/models/ci/application_setting.rb
+++ b/app/models/ci/application_setting.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: application_settings
+# Table name: ci_application_settings
 #
 #  id                :integer          not null, primary key
 #  all_broken_builds :boolean
@@ -12,9 +12,19 @@
 module Ci
   class ApplicationSetting < ActiveRecord::Base
     extend Ci::Model
-    
+
+    after_commit do
+      Rails.cache.write(cache_key, self)
+    end
+
+    def self.expire
+      Rails.cache.delete(cache_key)
+    end
+
     def self.current
-      Ci::ApplicationSetting.last
+      Rails.cache.fetch(cache_key) do
+        Ci::ApplicationSetting.last
+      end
     end
 
     def self.create_from_defaults
@@ -23,5 +33,9 @@ module Ci
         add_pusher: Settings.gitlab_ci['add_pusher'],
       )
     end
+
+    def self.cache_key
+      'ci_application_setting.last'
+    end
   end
 end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 0ec7e210321169ff7d2ce08a1ce60853047d5580..52ce1b920fae151f74b51a208da58cd6bf3b08f1 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: builds
+# Table name: ci_builds
 #
 #  id                 :integer          not null, primary key
 #  project_id         :integer
@@ -11,16 +11,24 @@
 #  updated_at         :datetime
 #  started_at         :datetime
 #  runner_id          :integer
-#  commit_id          :integer
 #  coverage           :float
+#  commit_id          :integer
 #  commands           :text
 #  job_id             :integer
 #  name               :string(255)
+#  deploy             :boolean          default(FALSE)
 #  options            :text
 #  allow_failure      :boolean          default(FALSE), not null
 #  stage              :string(255)
-#  deploy             :boolean          default(FALSE)
 #  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
 #
 
 module Ci
@@ -89,6 +97,8 @@ module Ci
 
     state_machine :status, initial: :pending do
       after_transition any => [:success, :failed, :canceled] do |build, transition|
+        return unless build.gl_project
+
         project = build.project
 
         if project.web_hooks?
diff --git a/app/models/ci/commit.rb b/app/models/ci/commit.rb
index e58420d82d462268fc3ffa3a92659bc58766ecac..971e899de847b6c6a5043c4f37d3404e080dd628 100644
--- a/app/models/ci/commit.rb
+++ b/app/models/ci/commit.rb
@@ -1,18 +1,19 @@
 # == Schema Information
 #
-# Table name: commits
+# Table name: ci_commits
 #
-#  id           :integer          not null, primary key
-#  project_id   :integer
-#  ref          :string(255)
-#  sha          :string(255)
-#  before_sha   :string(255)
-#  push_data    :text
-#  created_at   :datetime
-#  updated_at   :datetime
-#  tag          :boolean          default(FALSE)
-#  yaml_errors  :text
-#  committed_at :datetime
+#  id            :integer          not null, primary key
+#  project_id    :integer
+#  ref           :string(255)
+#  sha           :string(255)
+#  before_sha    :string(255)
+#  push_data     :text
+#  created_at    :datetime
+#  updated_at    :datetime
+#  tag           :boolean          default(FALSE)
+#  yaml_errors   :text
+#  committed_at  :datetime
+#  gl_project_id :integer
 #
 
 module Ci
@@ -187,13 +188,13 @@ module Ci
     end
 
     def config_processor
+      return nil unless ci_yaml_file
       @config_processor ||= Ci::GitlabCiYamlProcessor.new(ci_yaml_file, gl_project.path_with_namespace)
-    rescue Ci::GitlabCiYamlProcessor::ValidationError => e
+    rescue Ci::GitlabCiYamlProcessor::ValidationError, Psych::SyntaxError => e
       save_yaml_error(e.message)
       nil
-    rescue Exception => e
-      logger.error e.message + "\n" + e.backtrace.join("\n")
-      save_yaml_error("Undefined yaml error")
+    rescue
+      save_yaml_error("Undefined error")
       nil
     end
 
diff --git a/app/models/ci/event.rb b/app/models/ci/event.rb
index cac3a7a49c1222f1bb91da463e38590ab89176c3..8c39be4267767d52a6e69f2f78035a7417001fec 100644
--- a/app/models/ci/event.rb
+++ b/app/models/ci/event.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: events
+# Table name: ci_events
 #
 #  id          :integer          not null, primary key
 #  project_id  :integer
diff --git a/app/models/ci/project.rb b/app/models/ci/project.rb
index 4e806ca1a68eb5943739a403185841291fc4e231..669ee1cc0d2d3b6d46a7aacee74fde6c5153a26c 100644
--- a/app/models/ci/project.rb
+++ b/app/models/ci/project.rb
@@ -1,9 +1,9 @@
 # == Schema Information
 #
-# Table name: projects
+# Table name: ci_projects
 #
 #  id                       :integer          not null, primary key
-#  name                     :string(255)      not null
+#  name                     :string(255)
 #  timeout                  :integer          default(3600), not null
 #  created_at               :datetime
 #  updated_at               :datetime
@@ -66,30 +66,6 @@ module Ci
     class << self
       include Ci::CurrentSettings
 
-      def base_build_script
-        <<-eos
-  git submodule update --init
-  ls -la
-        eos
-      end
-
-      def parse(project)
-        params = {
-          gitlab_id:                project.id,
-          default_ref:              project.default_branch || 'master',
-          email_add_pusher:         current_application_settings.add_pusher,
-          email_only_broken_builds: current_application_settings.all_broken_builds,
-        }
-
-        project = Ci::Project.new(params)
-        project.build_missing_services
-        project
-      end
-
-      def already_added?(project)
-        where(gitlab_id: project.id).any?
-      end
-
       def unassigned(runner)
         joins("LEFT JOIN #{Ci::RunnerProject.table_name} ON #{Ci::RunnerProject.table_name}.project_id = #{Ci::Project.table_name}.id " \
           "AND #{Ci::RunnerProject.table_name}.runner_id = #{runner.id}").
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index b719ad3c87e987f7a5c828a607931b1edd9efe68..89710485811202a5b6bf3649177c0483f2bb997b 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runners
+# Table name: ci_runners
 #
 #  id           :integer          not null, primary key
 #  token        :string(255)
diff --git a/app/models/ci/runner_project.rb b/app/models/ci/runner_project.rb
index 44453ee4b41f230be3cb13b3a2bab45aac61700e..3f4fc43873eb2844793d4e6af0ebd246a4e64965 100644
--- a/app/models/ci/runner_project.rb
+++ b/app/models/ci/runner_project.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runner_projects
+# Table name: ci_runner_projects
 #
 #  id         :integer          not null, primary key
 #  runner_id  :integer          not null
diff --git a/app/models/ci/service.rb b/app/models/ci/service.rb
index ed5e3f940b66548fc4c15b6216ea37f2f821c096..8063c51e82b27861e3e0e4909a0e13ee4fb97776 100644
--- a/app/models/ci/service.rb
+++ b/app/models/ci/service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index fe224b7dc706ce55dfb88de289efef9e1412fbde..b73c35d5ae507156eb037aea782bc01535a48055 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: triggers
+# Table name: ci_triggers
 #
 #  id         :integer          not null, primary key
 #  token      :string(255)
diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb
index 29cd95533940dd5ae9b6e40b445aa7e88e91afa9..9973d2e5ade0a87e4f21d2107698c807ad3aae4c 100644
--- a/app/models/ci/trigger_request.rb
+++ b/app/models/ci/trigger_request.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: trigger_requests
+# Table name: ci_trigger_requests
 #
 #  id         :integer          not null, primary key
 #  trigger_id :integer          not null
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 7a542802fa6a0eb967fe5ba40c61920e7cd9c05e..b3d2b809e03c659a03df3a359fe385f453303d9b 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: variables
+# Table name: ci_variables
 #
 #  id                   :integer          not null, primary key
 #  project_id           :integer          not null
diff --git a/app/models/ci/web_hook.rb b/app/models/ci/web_hook.rb
index 8f03b0625da39ee5276071a0944631f318b5ea56..7ca16a1bde82fc7f2d603fe010d48ba6d844d4c5 100644
--- a/app/models/ci/web_hook.rb
+++ b/app/models/ci/web_hook.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: web_hooks
+# Table name: ci_web_hooks
 #
 #  id         :integer          not null, primary key
 #  url        :string(255)      not null
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index d346c5d35d21264f1f275c70fd828870fac56bc3..e70f4d37184adea8d9664a2aa6efb8c17932c6b8 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 class CommitStatus < ActiveRecord::Base
   self.table_name = 'ci_builds'
 
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 492a026add9ffdc2fd01420216a970adc850aa92..badeadfa4180cfb896f162cd4abc235b284624ea 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -8,6 +8,7 @@ module Issuable
   extend ActiveSupport::Concern
   include Participable
   include Mentionable
+  include StripAttribute
 
   included do
     belongs_to :author, class_name: "User"
@@ -35,6 +36,9 @@ module Issuable
     scope :order_milestone_due_desc, -> { joins(:milestone).reorder('milestones.due_date DESC, milestones.id DESC') }
     scope :order_milestone_due_asc, -> { joins(:milestone).reorder('milestones.due_date ASC, milestones.id ASC') }
 
+    scope :join_project, -> { joins(:project) }
+    scope :references_project, -> { references(:project) }
+
     delegate :name,
              :email,
              to: :author,
@@ -48,6 +52,7 @@ module Issuable
 
     attr_mentionable :title, :description
     participant :author, :assignee, :notes_with_associations
+    strip_attributes :title
   end
 
   module ClassMethods
@@ -89,39 +94,14 @@ module Issuable
     opened? || reopened?
   end
 
-  #
-  # Votes
-  #
-
-  # Return the number of -1 comments (downvotes)
+  # Deprecated. Still exists to preserve API compatibility.
   def downvotes
-    filter_superceded_votes(notes.select(&:downvote?), notes).size
+    0
   end
 
-  def downvotes_in_percent
-    if votes_count.zero?
-      0
-    else
-      100.0 - upvotes_in_percent
-    end
-  end
-
-  # Return the number of +1 comments (upvotes)
+  # Deprecated. Still exists to preserve API compatibility.
   def upvotes
-    filter_superceded_votes(notes.select(&:upvote?), notes).size
-  end
-
-  def upvotes_in_percent
-    if votes_count.zero?
-      0
-    else
-      100.0 / votes_count * upvotes
-    end
-  end
-
-  # Return the total number of votes
-  def votes_count
-    upvotes + downvotes
+    0
   end
 
   def subscribed?(user)
@@ -184,17 +164,8 @@ module Issuable
     notes.includes(:author, :project)
   end
 
-  private
-
-  def filter_superceded_votes(votes, notes)
-    filteredvotes = [] + votes
-
-    votes.each do |vote|
-      if vote.superceded?(notes)
-        filteredvotes.delete(vote)
-      end
-    end
-
-    filteredvotes
+  def updated_tasks
+    Taskable.get_updated_tasks(old_content: previous_changes['description'].first,
+                               new_content: description)
   end
 end
diff --git a/app/models/concerns/sortable.rb b/app/models/concerns/sortable.rb
index 913c747a1c3194bc5c98cd1f9558fdde019b520a..7391a77383c07db7fd9fff4ad0ee0a8de74bfc5a 100644
--- a/app/models/concerns/sortable.rb
+++ b/app/models/concerns/sortable.rb
@@ -8,8 +8,9 @@ module Sortable
   included do
     # By default all models should be ordered
     # by created_at field starting from newest
-    default_scope { order(id: :desc) }
+    default_scope { order_id_desc }
 
+    scope :order_id_desc, -> { reorder(id: :desc) }
     scope :order_created_desc, -> { reorder(created_at: :desc) }
     scope :order_created_asc, -> { reorder(created_at: :asc) }
     scope :order_updated_desc, -> { reorder(updated_at: :desc) }
diff --git a/app/models/concerns/strip_attribute.rb b/app/models/concerns/strip_attribute.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8806ebe897a246859dfbb8878a1671f982dc9593
--- /dev/null
+++ b/app/models/concerns/strip_attribute.rb
@@ -0,0 +1,34 @@
+# == Strip Attribute module
+#
+# Contains functionality to clean attributes before validation
+#
+# Usage:
+#
+#     class Milestone < ActiveRecord::Base
+#       strip_attributes :title
+#     end
+#
+#
+module StripAttribute
+  extend ActiveSupport::Concern
+
+  module ClassMethods
+    def strip_attributes(*attrs)
+      strip_attrs.concat(attrs)
+    end
+
+    def strip_attrs
+      @strip_attrs ||= []
+    end
+  end
+
+  included do
+    before_validation :strip_attributes
+  end
+
+  def strip_attributes
+    self.class.strip_attrs.each do |attr|
+      self[attr].strip! if self[attr] && self[attr].respond_to?(:strip!)
+    end
+  end
+end
diff --git a/app/models/concerns/taskable.rb b/app/models/concerns/taskable.rb
index 660e58b876db8d4acb48a1ca98ee9fb4a5b8e0d3..df2a9e3e84be06a8d7c8a8f01b3de00cb5382d4b 100644
--- a/app/models/concerns/taskable.rb
+++ b/app/models/concerns/taskable.rb
@@ -7,14 +7,39 @@ require 'task_list/filter'
 #
 # Used by MergeRequest and Issue
 module Taskable
+  COMPLETED    = 'completed'.freeze
+  INCOMPLETE   = 'incomplete'.freeze
+  ITEM_PATTERN = /
+    ^
+    (?:\s*[-+*]|(?:\d+\.))? # optional list prefix
+    \s*                     # optional whitespace prefix
+    (\[\s\]|\[[xX]\])       # checkbox
+    (\s.+)                  # followed by whitespace and some text.
+  /x
+
+  def self.get_tasks(content)
+    content.to_s.scan(ITEM_PATTERN).map do |checkbox, label|
+      # ITEM_PATTERN strips out the hyphen, but Item requires it. Rabble rabble.
+      TaskList::Item.new("- #{checkbox}", label.strip)
+    end
+  end
+
+  def self.get_updated_tasks(old_content:, new_content:)
+    old_tasks, new_tasks = get_tasks(old_content), get_tasks(new_content)
+
+    new_tasks.select.with_index do |new_task, i|
+      old_task = old_tasks[i]
+      next unless old_task
+
+      new_task.source == old_task.source && new_task.complete? != old_task.complete?
+    end
+  end
+
   # Called by `TaskList::Summary`
   def task_list_items
     return [] if description.blank?
 
-    @task_list_items ||= description.scan(TaskList::Filter::ItemPattern).collect do |item|
-      # ItemPattern strips out the hyphen, but Item requires it. Rabble rabble.
-      TaskList::Item.new("- #{item}")
-    end
+    @task_list_items ||= Taskable.get_tasks(description)
   end
 
   def tasks
diff --git a/app/models/event.rb b/app/models/event.rb
index bf64ac29d325759507834eef8be2c3c6d81c36dc..01d008035a585875f247bfa62274a7cffdc17d9a 100644
--- a/app/models/event.rb
+++ b/app/models/event.rb
@@ -63,6 +63,16 @@ class Event < ActiveRecord::Base
             Event::PUSHED, ["MergeRequest", "Issue"],
             [Event::CREATED, Event::CLOSED, Event::MERGED])
     end
+
+    def latest_update_time
+      row = select(:updated_at, :project_id).reorder(id: :desc).take
+
+      row ? row.updated_at : nil
+    end
+
+    def limit_recent(limit = 20, offset = nil)
+      recent.limit(limit).offset(offset)
+    end
   end
 
   def proper?
@@ -191,7 +201,7 @@ class Event < ActiveRecord::Base
     elsif commented?
       "commented on"
     elsif created_project?
-      if project.import?
+      if project.external_import?
         "imported"
       else
         "created"
diff --git a/app/models/generic_commit_status.rb b/app/models/generic_commit_status.rb
index fa54e3540d07bd99637c76b65d7b53ba4acc58c5..12c934e24949af9494f1247200d7f7b55c88da94 100644
--- a/app/models/generic_commit_status.rb
+++ b/app/models/generic_commit_status.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 class GenericCommitStatus < CommitStatus
   before_validation :set_default_values
 
diff --git a/app/models/global_label.rb b/app/models/global_label.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0171f7d54b7d6a56157d4bdcd4a1b664aba71fd6
--- /dev/null
+++ b/app/models/global_label.rb
@@ -0,0 +1,17 @@
+class GlobalLabel
+  attr_accessor :title, :labels
+  alias_attribute :name, :title
+
+  def self.build_collection(labels)
+    labels = labels.group_by(&:title)
+
+    labels.map do |title, label|
+      new(title, label)
+    end
+  end
+
+  def initialize(title, labels)
+    @title = title
+    @labels = labels
+  end
+end
diff --git a/app/models/group_milestone.rb b/app/models/global_milestone.rb
similarity index 76%
rename from app/models/group_milestone.rb
rename to app/models/global_milestone.rb
index 91844da62e22c6c7a2b3cfc1f799e301f899fd9c..1321ccd963fb449491967fe1365c32394899e56a 100644
--- a/app/models/group_milestone.rb
+++ b/app/models/global_milestone.rb
@@ -1,7 +1,15 @@
-class GroupMilestone
+class GlobalMilestone
   attr_accessor :title, :milestones
   alias_attribute :name, :title
 
+  def self.build_collection(milestones)
+    milestones = milestones.group_by(&:title)
+
+    milestones.map do |title, milestones|
+      new(title, milestones)
+    end
+  end
+
   def initialize(title, milestones)
     @title = title
     @milestones = milestones
@@ -10,7 +18,7 @@ class GroupMilestone
   def safe_title
     @title.parameterize
   end
-  
+
   def projects
     milestones.map { |milestone| milestone.project }
   end
@@ -60,15 +68,15 @@ class GroupMilestone
   end
 
   def issues
-    @group_issues ||= milestones.map(&:issues).flatten.group_by(&:state)
+    @issues ||= milestones.map(&:issues).flatten.group_by(&:state)
   end
 
   def merge_requests
-    @group_merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
+    @merge_requests ||= milestones.map(&:merge_requests).flatten.group_by(&:state)
   end
 
   def participants
-    @group_participants ||= milestones.map(&:participants).flatten.compact.uniq
+    @participants ||= milestones.map(&:participants).flatten.compact.uniq
   end
 
   def opened_issues
@@ -86,4 +94,8 @@ class GroupMilestone
   def closed_merge_requests
     merge_requests.values_at("closed", "merged", "locked").compact.flatten
   end
+
+  def complete?
+    total_items_count == closed_items_count
+  end
 end
diff --git a/app/models/group.rb b/app/models/group.rb
index 34904af3b5bde7dd1a7f7f9da8dac0d4e4b128c6..1b5b875a19e3ff8dbaef174445f5a26ca698a5ce 100644
--- a/app/models/group.rb
+++ b/app/models/group.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'carrierwave/orm/activerecord'
@@ -19,8 +20,9 @@ require 'file_size_validator'
 class Group < Namespace
   include Gitlab::ConfigHelper
   include Referable
-
+  
   has_many :group_members, dependent: :destroy, as: :source, class_name: 'GroupMember'
+  alias_method :members, :group_members
   has_many :users, through: :group_members
 
   validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? }
@@ -47,6 +49,14 @@ class Group < Namespace
     def reference_pattern
       User.reference_pattern
     end
+
+    def public_and_given_groups(ids)
+      where('public IS TRUE OR namespaces.id IN (?)', ids)
+    end
+
+    def visible_to_user(user)
+      where(id: user.authorized_groups.select(:id).reorder(nil))
+    end
   end
 
   def to_reference(_from_project = nil)
@@ -109,10 +119,6 @@ class Group < Namespace
     has_owner?(user) && owners.size == 1
   end
 
-  def members
-    group_members
-  end
-
   def avatar_type
     unless self.avatar.image?
       self.errors.add :avatar, "only images allowed"
diff --git a/app/models/group_label.rb b/app/models/group_label.rb
deleted file mode 100644
index 0fc39cb87716245bbcd36c658f92b2f0eb5e0e37..0000000000000000000000000000000000000000
--- a/app/models/group_label.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class GroupLabel
-  attr_accessor :title, :labels
-  alias_attribute :name, :title
-
-  def initialize(title, labels)
-    @title = title
-    @labels = labels
-  end
-end
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index ca7066b959aae42ff43a5924d24e179df7775a23..337b30971260d33555816c8f9ed0661799c849b6 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class ProjectHook < WebHook
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index b55e217975f90c911a27ccffadb5ee25f70e3f34..09bb3ee52a2dc8d82207a550f2602f8a8830f88c 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class ServiceHook < WebHook
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index 6fb2d4210268afb9a5be7c9d7b24cf20b9d77a89..2f63c59b07eaad722898ac369d51d0786844875d 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class SystemHook < WebHook
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index a078accbdbdca9a74b3f2c1a6e534506f839d19c..d6c6f415c4a1fd23e27686222589dbd70c046cb6 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -2,18 +2,19 @@
 #
 # Table name: web_hooks
 #
-#  id                    :integer          not null, primary key
-#  url                   :string(255)
-#  project_id            :integer
-#  created_at            :datetime
-#  updated_at            :datetime
-#  type                  :string(255)      default("ProjectHook")
-#  service_id            :integer
-#  push_events           :boolean          default(TRUE), not null
-#  issues_events         :boolean          default(FALSE), not null
-#  merge_requests_events :boolean          default(FALSE), not null
-#  tag_push_events       :boolean          default(FALSE)
-#  note_events           :boolean          default(FALSE), not null
+#  id                      :integer          not null, primary key
+#  url                     :string(255)
+#  project_id              :integer
+#  created_at              :datetime
+#  updated_at              :datetime
+#  type                    :string(255)      default("ProjectHook")
+#  service_id              :integer
+#  push_events             :boolean          default(TRUE), not null
+#  issues_events           :boolean          default(FALSE), not null
+#  merge_requests_events   :boolean          default(FALSE), not null
+#  tag_push_events         :boolean          default(FALSE)
+#  note_events             :boolean          default(FALSE), not null
+#  enable_ssl_verification :boolean          default(TRUE)
 #
 
 class WebHook < ActiveRecord::Base
diff --git a/app/models/label.rb b/app/models/label.rb
index 1bb4b5f55cfa6e89f32e3d354e0abd9b3ad6c83a..bef6063fe88f3e9f8744c9dd91871dfeb15cf4fb 100644
--- a/app/models/label.rb
+++ b/app/models/label.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 class Label < ActiveRecord::Base
@@ -16,7 +17,7 @@ class Label < ActiveRecord::Base
   # Requests that have no label assigned.
   LabelStruct = Struct.new(:title, :name)
   None = LabelStruct.new('No Label', 'No Label')
-  Any = LabelStruct.new('Any', '')
+  Any = LabelStruct.new('Any Label', '')
 
   DEFAULT_COLOR = '#428BCA'
 
diff --git a/app/models/lfs_object.rb b/app/models/lfs_object.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3c1426f59d08c085e17e92fdc0d8dc0a94af9e99
--- /dev/null
+++ b/app/models/lfs_object.rb
@@ -0,0 +1,8 @@
+class LfsObject < ActiveRecord::Base
+  has_many :lfs_objects_projects, dependent: :destroy
+  has_many :projects, through: :lfs_objects_projects
+
+  validates :oid, presence: true, uniqueness: true
+
+  mount_uploader :file, LfsObjectUploader
+end
diff --git a/app/models/lfs_objects_project.rb b/app/models/lfs_objects_project.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0fd5f089db9076d03d7c17fcea5518964d688809
--- /dev/null
+++ b/app/models/lfs_objects_project.rb
@@ -0,0 +1,8 @@
+class LfsObjectsProject < ActiveRecord::Base
+  belongs_to :project
+  belongs_to :lfs_object
+
+  validates :lfs_object_id, presence: true
+  validates :lfs_object_id, uniqueness: { scope: [:project_id], message: "already exists in project" }
+  validates :project_id, presence: true
+end
diff --git a/app/models/member.rb b/app/models/member.rb
index cae8caa23fb2770299ee935b82f48edecd22f39b..28aee2e379963224b4459c4b5251d10464528d63 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -30,13 +30,22 @@ class Member < ActiveRecord::Base
 
   validates :user, presence: true, unless: :invite?
   validates :source, presence: true
-  validates :user_id, uniqueness: { scope: [:source_type, :source_id], 
+  validates :user_id, uniqueness: { scope: [:source_type, :source_id],
                                     message: "already exists in source",
                                     allow_nil: true }
   validates :access_level, inclusion: { in: Gitlab::Access.all_values }, presence: true
-  validates :invite_email,  presence: { if: :invite? }, 
-                            email: { strict_mode: true, allow_nil: true }, 
-                            uniqueness: { scope: [:source_type, :source_id], allow_nil: true }
+  validates :invite_email,
+    presence: {
+      if: :invite?
+    },
+    email: {
+      strict_mode: true,
+      allow_nil: true
+    },
+    uniqueness: {
+      scope: [:source_type, :source_id],
+      allow_nil: true
+    }
 
   scope :invite, -> { where(user_id: nil) }
   scope :non_invite, -> { where("user_id IS NOT NULL") }
@@ -73,7 +82,7 @@ class Member < ActiveRecord::Base
 
     def add_user(members, user_id, access_level, current_user = nil)
       user = user_for_id(user_id)
-      
+
       # `user` can be either a User object or an email to be invited
       if user.is_a?(User)
         member = members.find_or_initialize_by(user_id: user.id)
@@ -82,10 +91,21 @@ class Member < ActiveRecord::Base
         member.invite_email = user
       end
 
-      member.created_by ||= current_user
-      member.access_level = access_level
+      if can_update_member?(current_user, member)
+        member.created_by ||= current_user
+        member.access_level = access_level
+
+        member.save
+      end
+    end
+
+    private
 
-      member.save
+    def can_update_member?(current_user, member)
+      # There is no current user for bulk actions, in which case anything is allowed
+      !current_user ||
+        current_user.can?(:update_group_member, member) ||
+        current_user.can?(:update_project_member, member)
     end
   end
 
@@ -95,7 +115,7 @@ class Member < ActiveRecord::Base
 
   def accept_invite!(new_user)
     return false unless invite?
-    
+
     self.invite_token = nil
     self.invite_accepted_at = Time.now.utc
 
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 85f37e49e625faed80368f481672782d36dd0b7a..1b3d6079d2cdeeef0e9d9926a446cfb8276cbb32 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 require Rails.root.join("app/models/commit")
@@ -40,7 +41,7 @@ class MergeRequest < ActiveRecord::Base
   after_create :create_merge_request_diff
   after_update :update_merge_request_diff
 
-  delegate :commits, :diffs, to: :merge_request_diff, prefix: nil
+  delegate :commits, :diffs, :diffs_no_whitespace, to: :merge_request_diff, prefix: nil
 
   # When this attribute is true some MR validation is ignored
   # It allows us to close or modify broken merge requests
@@ -133,6 +134,9 @@ class MergeRequest < ActiveRecord::Base
   scope :closed, -> { with_state(:closed) }
   scope :closed_and_merged, -> { with_states(:closed, :merged) }
 
+  scope :join_project, -> { joins(:target_project) }
+  scope :references_project, -> { references(:target_project) }
+
   def self.reference_prefix
     '!'
   end
@@ -472,7 +476,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def ci_commit
-    if last_commit
+    if last_commit and source_project
       source_project.ci_commit(last_commit.id)
     end
   end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index 6575d0bc81f1ea118a37a2f9c7bc8f4191ef2b98..c499a4b5b4c8006f834fcf3ffcaee513d89120ec 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -19,7 +19,7 @@ class MergeRequestDiff < ActiveRecord::Base
   # Prevent store of diff if commits amount more then 500
   COMMITS_SAFE_SIZE = 500
 
-  attr_reader :commits, :diffs
+  attr_reader :commits, :diffs, :diffs_no_whitespace
 
   belongs_to :merge_request
 
@@ -47,6 +47,20 @@ class MergeRequestDiff < ActiveRecord::Base
     @diffs ||= (load_diffs(st_diffs) || [])
   end
 
+  def diffs_no_whitespace
+    # Get latest sha of branch from source project
+    source_sha = merge_request.source_project.commit(source_branch).sha
+
+    compare_result = Gitlab::CompareResult.new(
+      Gitlab::Git::Compare.new(
+        merge_request.target_project.repository.raw_repository,
+        merge_request.target_branch,
+        source_sha,
+      ), { ignore_whitespace_change: true }
+    )
+    @diffs_no_whitespace ||= load_diffs(dump_commits(compare_result.diffs))
+  end
+
   def commits
     @commits ||= load_commits(st_commits || [])
   end
diff --git a/app/models/milestone.rb b/app/models/milestone.rb
index 2ff16e2825c71ff7eb89eb905505048ebeedbd08..d8c7536cd316fbd907116c3e0d671802dd2990de 100644
--- a/app/models/milestone.rb
+++ b/app/models/milestone.rb
@@ -16,12 +16,13 @@
 class Milestone < ActiveRecord::Base
   # Represents a "No Milestone" state used for filtering Issues and Merge
   # Requests that have no milestone assigned.
-  MilestoneStruct = Struct.new(:title, :name)
-  None = MilestoneStruct.new('No Milestone', 'No Milestone')
-  Any = MilestoneStruct.new('Any', '')
+  MilestoneStruct = Struct.new(:title, :name, :id)
+  None = MilestoneStruct.new('No Milestone', 'No Milestone', 0)
+  Any = MilestoneStruct.new('Any Milestone', '', -1)
 
   include InternalId
   include Sortable
+  include StripAttribute
 
   belongs_to :project
   has_many :issues
@@ -35,6 +36,8 @@ class Milestone < ActiveRecord::Base
   validates :title, presence: true
   validates :project, presence: true
 
+  strip_attributes :title
+
   state_machine :state, initial: :active do
     event :close do
       transition active: :closed
diff --git a/app/models/namespace.rb b/app/models/namespace.rb
index 5782e649f8be15100eadec612886b6ea2f26e5ae..20b92e68d6100176484870b310972546b7af954a 100644
--- a/app/models/namespace.rb
+++ b/app/models/namespace.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 class Namespace < ActiveRecord::Base
diff --git a/app/models/note.rb b/app/models/note.rb
index 0b3aa30abd745187bc656439d4cddac7969a532a..1c6345e735c4b94154aecce51de775996354ce77 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -40,16 +40,20 @@ class Note < ActiveRecord::Base
   delegate :name, :email, to: :author, prefix: true
 
   validates :note, :project, presence: true
+  validates :note, uniqueness: { scope: [:author, :noteable_type, :noteable_id] }, if: ->(n) { n.is_award }
   validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
   # Attachments are deprecated and are handled by Markdown uploader
   validates :attachment, file_size: { maximum: :max_attachment_size }
 
   validates :noteable_id, presence: true, if: ->(n) { n.noteable_type.present? && n.noteable_type != 'Commit' }
   validates :commit_id, presence: true, if: ->(n) { n.noteable_type == 'Commit' }
+  validates :author, presence: true
 
   mount_uploader :attachment, AttachmentUploader
 
   # Scopes
+  scope :awards, ->{ where(is_award: true) }
+  scope :nonawards, ->{ where(is_award: false) }
   scope :for_commit_id, ->(commit_id) { where(noteable_type: "Commit", commit_id: commit_id) }
   scope :inline, ->{ where("line_code IS NOT NULL") }
   scope :not_inline, ->{ where(line_code: [nil, '']) }
@@ -97,6 +101,12 @@ class Note < ActiveRecord::Base
     def search(query)
       where("LOWER(note) like :query", query: "%#{query.downcase}%")
     end
+
+    def grouped_awards
+      awards.select(:note).distinct.map do |note|
+        [ note.note, where(note: note.note) ]
+      end
+    end
   end
 
   def cross_reference?
@@ -288,44 +298,6 @@ class Note < ActiveRecord::Base
     nil
   end
 
-  DOWNVOTES = %w(-1 :-1: :thumbsdown: :thumbs_down_sign:)
-
-  # Check if the note is a downvote
-  def downvote?
-    votable? && note.start_with?(*DOWNVOTES)
-  end
-
-  UPVOTES = %w(+1 :+1: :thumbsup: :thumbs_up_sign:)
-
-  # Check if the note is an upvote
-  def upvote?
-    votable? && note.start_with?(*UPVOTES)
-  end
-
-  def superceded?(notes)
-    return false unless vote?
-
-    notes.each do |note|
-      next if note == self
-
-      if note.vote? &&
-        self[:author_id] == note[:author_id] &&
-        self[:created_at] <= note[:created_at]
-        return true
-      end
-    end
-
-    false
-  end
-
-  def vote?
-    upvote? || downvote?
-  end
-
-  def votable?
-    for_issue? || (for_merge_request? && !for_diff_line?)
-  end
-
   # Mentionable override.
   def gfm_reference(from_project = nil)
     noteable.gfm_reference(from_project)
@@ -363,6 +335,16 @@ class Note < ActiveRecord::Base
     read_attribute(:system)
   end
 
+  # Deprecated. Still exists to preserve API compatibility.
+  def downvote?
+    false
+  end
+
+  # Deprecated. Still exists to preserve API compatibility.
+  def upvote?
+    false
+  end
+
   def editable?
     !system?
   end
diff --git a/app/models/project.rb b/app/models/project.rb
index a01246a4c7300998aba2d6feb16821bc4b5205ad..e28a6977556a306c12fddfa989982f5ba918456e 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -44,7 +44,6 @@ class Project < ActiveRecord::Base
   include CaseSensitivity
 
   extend Gitlab::ConfigHelper
-  extend Enumerize
 
   UNKNOWN_IMPORT_URL = 'http://unknown.git'
 
@@ -52,6 +51,7 @@ class Project < ActiveRecord::Base
   default_value_for :visibility_level, gitlab_config_features.visibility_level
   default_value_for :issues_enabled, gitlab_config_features.issues
   default_value_for :merge_requests_enabled, gitlab_config_features.merge_requests
+  default_value_for :builds_enabled, gitlab_config_features.builds
   default_value_for :wiki_enabled, gitlab_config_features.wiki
   default_value_for :wall_enabled, false
   default_value_for :snippets_enabled, gitlab_config_features.snippets
@@ -123,6 +123,8 @@ class Project < ActiveRecord::Base
   has_many :ci_commits, dependent: :destroy, class_name: 'Ci::Commit', foreign_key: :gl_project_id
   has_many :ci_builds, through: :ci_commits, source: :builds, dependent: :destroy, class_name: 'Ci::Build'
   has_many :releases, dependent: :destroy
+  has_many :lfs_objects_projects, dependent: :destroy
+  has_many :lfs_objects, through: :lfs_objects_projects
 
   has_one :import_data, dependent: :destroy, class_name: "ProjectImportData"
   has_one :gitlab_ci_project, dependent: :destroy, class_name: "Ci::Project", foreign_key: :gitlab_id
@@ -283,6 +285,10 @@ class Project < ActiveRecord::Base
 
       joins(join_body).reorder('join_note_counts.amount DESC')
     end
+
+    def visible_to_user(user)
+      where(id: user.authorized_projects.select(:id).reorder(nil))
+    end
   end
 
   def team
@@ -307,15 +313,17 @@ class Project < ActiveRecord::Base
 
   def add_import_job
     if forked?
-      unless RepositoryForkWorker.perform_async(id, forked_from_project.path_with_namespace, self.namespace.path)
-        import_fail
-      end
+      RepositoryForkWorker.perform_async(self.id, forked_from_project.path_with_namespace, self.namespace.path)
     else
-      RepositoryImportWorker.perform_async(id)
+      RepositoryImportWorker.perform_async(self.id)
     end
   end
 
   def clear_import_data
+    update(import_error: nil)
+
+    ProjectCacheWorker.perform_async(self.id)
+
     self.import_data.destroy if self.import_data
   end
 
@@ -343,6 +351,14 @@ class Project < ActiveRecord::Base
     import_status == 'finished'
   end
 
+  def safe_import_url
+    result = URI.parse(self.import_url)
+    result.password = '*****' unless result.password.nil?
+    result.to_s
+  rescue
+    original_url
+  end
+
   def check_limit
     unless creator.can_create_project? or namespace.kind == 'group'
       errors[:limit_reached] << ("Your project limit is #{creator.projects_limit} projects! Please contact your administrator to increase it")
@@ -457,10 +473,6 @@ class Project < ActiveRecord::Base
     list.find { |service| service.to_param == name }
   end
 
-  def gitlab_ci?
-    gitlab_ci_service && gitlab_ci_service.active && gitlab_ci_project.present?
-  end
-
   def ci_services
     services.select { |service| service.category == :ci }
   end
@@ -784,9 +796,33 @@ class Project < ActiveRecord::Base
     )
   end
 
-  def enable_ci
+  # TODO: this should be migrated to Project table,
+  # the same as issues_enabled
+  def builds_enabled
+    gitlab_ci_service && gitlab_ci_service.active
+  end
+
+  def builds_enabled?
+    builds_enabled
+  end
+
+  def builds_enabled=(value)
     service = gitlab_ci_service || create_gitlab_ci_service
-    service.active = true
+    service.active = value
     service.save
   end
+
+  def enable_ci
+    self.builds_enabled = true
+  end
+
+  def unlink_fork
+    if forked?
+      forked_from_project.lfs_objects.find_each do |lfs_object|
+        lfs_object.projects << self
+      end
+
+      forked_project_link.destroy
+    end
+  end
 end
diff --git a/app/models/project_services/buildkite_service.rb b/app/models/project_services/buildkite_service.rb
index 40058b53df556742190f3566bb61cb6ebdf5d8f8..199ee3a9d0df2b53cc3ba5bd3fe9f548c54be5e8 100644
--- a/app/models/project_services/buildkite_service.rb
+++ b/app/models/project_services/buildkite_service.rb
@@ -37,7 +37,7 @@ class BuildkiteService < CiService
   def compose_service_hook
     hook = service_hook || build_service_hook
     hook.url = webhook_url
-    hook.enable_ssl_verification = enable_ssl_verification
+    hook.enable_ssl_verification = !!enable_ssl_verification
     hook.save
   end
 
diff --git a/app/models/project_services/ci/hip_chat_service.rb b/app/models/project_services/ci/hip_chat_service.rb
index f17993d9f3bc7df6451f1fd2c3d801cfad4b231e..0df03890efb2f838a2f54ba3430ff64ba4f6cdc2 100644
--- a/app/models/project_services/ci/hip_chat_service.rb
+++ b/app/models/project_services/ci/hip_chat_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/app/models/project_services/ci/mail_service.rb b/app/models/project_services/ci/mail_service.rb
index fd1933010015022130be19cefe1a3e36500ea9bf..bb961d069720defd4cc3f226eeff97dd41d6533c 100644
--- a/app/models/project_services/ci/mail_service.rb
+++ b/app/models/project_services/ci/mail_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
@@ -64,9 +64,9 @@ module Ci
       build.project_recipients.each do |recipient|
         case build.status.to_sym
         when :success
-          mailer.build_success_email(build.id, recipient)
+          mailer.build_success_email(build.id, recipient).deliver_later
         when :failed
-          mailer.build_fail_email(build.id, recipient)
+          mailer.build_fail_email(build.id, recipient).deliver_later
         end
       end
     end
@@ -78,7 +78,7 @@ module Ci
     end
 
     def mailer
-      Ci::Notify.delay
+      Ci::Notify
     end
   end
 end
diff --git a/app/models/project_services/ci/slack_service.rb b/app/models/project_services/ci/slack_service.rb
index ee8e49888263c31d1daa95a07c785feaf587576e..7064bfe78db59a2615639b27315da6bf3f7d567b 100644
--- a/app/models/project_services/ci/slack_service.rb
+++ b/app/models/project_services/ci/slack_service.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/app/models/project_services/drone_ci_service.rb b/app/models/project_services/drone_ci_service.rb
index c240213200ddb077006042f0c9c17d18e8154daf..06c3922593c6b996c4a5f9db49efbbe309c67107 100644
--- a/app/models/project_services/drone_ci_service.rb
+++ b/app/models/project_services/drone_ci_service.rb
@@ -32,7 +32,9 @@ class DroneCiService < CiService
 
   def compose_service_hook
     hook = service_hook || build_service_hook
-    hook.enable_ssl_verification = enable_ssl_verification
+    # If using a service template, project may not be available
+    hook.url = [drone_url, "/api/hook", "?owner=#{project.namespace.path}", "&name=#{project.path}", "&access_token=#{token}"].join if project
+    hook.enable_ssl_verification = !!enable_ssl_verification
     hook.save
   end
 
diff --git a/app/models/project_services/gitlab_ci_service.rb b/app/models/project_services/gitlab_ci_service.rb
index 095d04e0df4c0a2959e796d849ab583cdfa487c8..234e8e8b5808e11683dd22c76f2854da77c5afc6 100644
--- a/app/models/project_services/gitlab_ci_service.rb
+++ b/app/models/project_services/gitlab_ci_service.rb
@@ -30,6 +30,7 @@ class GitlabCiService < CiService
   end
 
   def ensure_gitlab_ci_project
+    return unless project
     project.ensure_gitlab_ci_project
   end
 
@@ -54,7 +55,7 @@ class GitlabCiService < CiService
   end
 
   def get_ci_commit(sha, ref)
-    Ci::Project.find(project.gitlab_ci_project).commits.find_by_sha!(sha)
+    Ci::Project.find(project.gitlab_ci_project.id).commits.find_by_sha!(sha)
   end
 
   def commit_status(sha, ref)
diff --git a/app/models/project_services/slack_service/note_message.rb b/app/models/project_services/slack_service/note_message.rb
index 074478b292da5ef73cd6cd7860bf0893404d1cfa..b15d9a1467771833aa698e02496ca91398a39fd5 100644
--- a/app/models/project_services/slack_service/note_message.rb
+++ b/app/models/project_services/slack_service/note_message.rb
@@ -45,30 +45,27 @@ class SlackService
     def create_commit_note(commit)
       commit_sha = commit[:id]
       commit_sha = Commit.truncate_sha(commit_sha)
-      commit_link = "[commit #{commit_sha}](#{@note_url})"
-      title = format_title(commit[:message])
-      @message = "#{@user_name} commented on #{commit_link} in #{project_link}: *#{title}*"
+      commented_on_message(
+        "[commit #{commit_sha}](#{@note_url})",
+        format_title(commit[:message]))
     end
 
     def create_issue_note(issue)
-      issue_iid = issue[:iid]
-      note_link = "[issue ##{issue_iid}](#{@note_url})"
-      title = format_title(issue[:title])
-      @message = "#{@user_name} commented on #{note_link} in #{project_link}: *#{title}*"
+      commented_on_message(
+        "[issue ##{issue[:iid]}](#{@note_url})",
+        format_title(issue[:title]))
     end
 
     def create_merge_note(merge_request)
-      merge_request_id = merge_request[:iid]
-      merge_request_link = "[merge request ##{merge_request_id}](#{@note_url})"
-      title = format_title(merge_request[:title])
-      @message = "#{@user_name} commented on #{merge_request_link} in #{project_link}: *#{title}*"
+      commented_on_message(
+        "[merge request ##{merge_request[:iid]}](#{@note_url})",
+        format_title(merge_request[:title]))
     end
 
     def create_snippet_note(snippet)
-      snippet_id = snippet[:id]
-      snippet_link = "[snippet ##{snippet_id}](#{@note_url})"
-      title = format_title(snippet[:title])
-      @message = "#{@user_name} commented on #{snippet_link} in #{project_link}: *#{title}*"
+      commented_on_message(
+        "[snippet ##{snippet[:id]}](#{@note_url})",
+        format_title(snippet[:title]))
     end
 
     def description_message
@@ -78,5 +75,9 @@ class SlackService
     def project_link
       "[#{@project_name}](#{@project_url})"
     end
+
+    def commented_on_message(target_link, title)
+      @message = "#{@user_name} commented on #{target_link} in #{project_link}: *#{title}*"
+    end
   end
 end
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index 231973fa543353507d42e8d11df59945da4886d4..b5fec38378b9851200eea31ed1283d03527a4cd9 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -86,6 +86,8 @@ class ProjectWiki
     commit = commit_details(:created, message, title)
 
     wiki.write_page(title, format, content, commit)
+
+    update_project_activity
   rescue Gollum::DuplicatePageError => e
     @error_message = "Duplicate page: #{e.message}"
     return false
@@ -95,10 +97,14 @@ class ProjectWiki
     commit = commit_details(:updated, message, page.title)
 
     wiki.update_page(page, page.name, format, content, commit)
+
+    update_project_activity
   end
 
   def delete_page(page, message = nil)
     wiki.delete_page(page, commit_details(:deleted, message, page.title))
+
+    update_project_activity
   end
 
   def page_title_and_dir(title)
@@ -146,4 +152,8 @@ class ProjectWiki
   def path_to_repo
     @path_to_repo ||= File.join(Gitlab.config.gitlab_shell.repos_path, "#{path_with_namespace}.git")
   end
+
+  def update_project_activity
+    @project.touch(:last_activity_at)
+  end
 end
diff --git a/app/models/release.rb b/app/models/release.rb
index e196b84eb18845cbbfdf8b2acedd6309ab3931f2..89f70278af5bac889e5af3aa79a4a0de9e8bbbc6 100644
--- a/app/models/release.rb
+++ b/app/models/release.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
 class Release < ActiveRecord::Base
   belongs_to :project
 
diff --git a/app/models/repository.rb b/app/models/repository.rb
index 2ba6a627e461802c2ed982fe778d8e8bebf8de5f..4186ef295c9b18551271be1e682d5f68a375863d 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -6,7 +6,7 @@ class Repository
 
   include Gitlab::ShellAdapter
 
-  attr_accessor :raw_repository, :path_with_namespace, :project
+  attr_accessor :path_with_namespace, :project
 
   def self.clean_old_archives
     repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path
@@ -19,14 +19,18 @@ class Repository
   def initialize(path_with_namespace, default_branch = nil, project = nil)
     @path_with_namespace = path_with_namespace
     @project = project
+  end
 
-    if path_with_namespace
-      @raw_repository = Gitlab::Git::Repository.new(path_to_repo)
-      @raw_repository.autocrlf = :input
-    end
+  def raw_repository
+    return nil unless path_with_namespace
 
-  rescue Gitlab::Git::Repository::NoRepository
-    nil
+    @raw_repository ||= begin
+      repo = Gitlab::Git::Repository.new(path_to_repo)
+      repo.autocrlf = :input
+      repo
+    rescue Gitlab::Git::Repository::NoRepository
+      nil
+    end
   end
 
   # Return absolute path to repository
@@ -105,29 +109,25 @@ class Repository
   end
 
   def add_branch(branch_name, ref)
-    cache.expire(:branch_names)
-    @branches = nil
+    expire_branches_cache
 
     gitlab_shell.add_branch(path_with_namespace, branch_name, ref)
   end
 
   def add_tag(tag_name, ref, message = nil)
-    cache.expire(:tag_names)
-    @tags = nil
+    expire_tags_cache
 
     gitlab_shell.add_tag(path_with_namespace, tag_name, ref, message)
   end
 
   def rm_branch(branch_name)
-    cache.expire(:branch_names)
-    @branches = nil
+    expire_branches_cache
 
     gitlab_shell.rm_branch(path_with_namespace, branch_name)
   end
 
   def rm_tag(tag_name)
-    cache.expire(:tag_names)
-    @tags = nil
+    expire_tags_cache
 
     gitlab_shell.rm_tag(path_with_namespace, tag_name)
   end
@@ -190,6 +190,16 @@ class Repository
     end
   end
 
+  def expire_tags_cache
+    cache.expire(:tag_names)
+    @tags = nil
+  end
+
+  def expire_branches_cache
+    cache.expire(:branch_names)
+    @branches = nil
+  end
+
   def expire_cache
     cache_keys.each do |key|
       cache.expire(key)
@@ -380,8 +390,8 @@ class Repository
     end
   end
 
-  def branch_names_contains(sha)
-    args = %W(#{Gitlab.config.git.bin_path} branch --contains #{sha})
+  def refs_contains_sha(ref_type, sha)
+    args = %W(#{Gitlab.config.git.bin_path} #{ref_type} --contains #{sha})
     names = Gitlab::Popen.popen(args, path_to_repo).first
 
     if names.respond_to?(:split)
@@ -397,21 +407,12 @@ class Repository
     end
   end
 
-  def tag_names_contains(sha)
-    args = %W(#{Gitlab.config.git.bin_path} tag --contains #{sha})
-    names = Gitlab::Popen.popen(args, path_to_repo).first
-
-    if names.respond_to?(:split)
-      names = names.split("\n").map(&:strip)
-
-      names.each do |name|
-        name.slice! '* '
-      end
+  def branch_names_contains(sha)
+    refs_contains_sha('branch', sha)
+  end
 
-      names
-    else
-      []
-    end
+  def tag_names_contains(sha)
+    refs_contains_sha('tag', sha)
   end
 
   def branches
@@ -527,7 +528,7 @@ class Repository
     root_ref_commit = commit(root_ref)
 
     if branch_commit
-      rugged.merge_base(root_ref_commit.id, branch_commit.id) == branch_commit.id
+      is_ancestor?(branch_commit.id, root_ref_commit.id)
     else
       nil
     end
@@ -537,6 +538,11 @@ class Repository
     rugged.merge_base(first_commit_id, second_commit_id)
   end
 
+  def is_ancestor?(ancestor_id, descendant_id)
+    merge_base(ancestor_id, descendant_id) == ancestor_id
+  end
+
+
   def search_files(query, ref)
     offset = 2
     args = %W(#{Gitlab.config.git.bin_path} grep -i -n --before-context #{offset} --after-context #{offset} -e #{query} #{ref || root_ref})
@@ -599,9 +605,13 @@ class Repository
 
     # Run GitLab pre-receive hook
     pre_receive_hook = Gitlab::Git::Hook.new('pre-receive', path_to_repo)
-    status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref)
+    pre_receive_hook_status = pre_receive_hook.trigger(gl_id, oldrev, newrev, ref)
+
+    # Run GitLab update hook
+    update_hook = Gitlab::Git::Hook.new('update', path_to_repo)
+    update_hook_status = update_hook.trigger(gl_id, oldrev, newrev, ref)
 
-    if status
+    if pre_receive_hook_status && update_hook_status
       if was_empty
         # Create branch
         rugged.references.create(ref, newrev)
@@ -624,7 +634,7 @@ class Repository
       # Remove tmp ref and return error to user
       rugged.references.delete(tmp_ref)
 
-      raise PreReceiveError.new('Commit was rejected by pre-receive hook')
+      raise PreReceiveError.new('Commit was rejected by git hook')
     end
   end
 
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 3eed5c16e45c41fec23cf93c83eba0a0d0aac01d..d8fe65b06f6d34238f0316e2bfa7c323394ad2f2 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -17,9 +17,8 @@ class SentNotification < ActiveRecord::Base
   belongs_to :noteable, polymorphic: true
   belongs_to :recipient, class_name: "User"
 
-  validate :project, :recipient, :reply_key, presence: true
-  validate :reply_key, uniqueness: true
-
+  validates :project, :recipient, :reply_key, presence: true
+  validates :reply_key, uniqueness: true
   validates :noteable_id, presence: true, unless: :for_commit?
   validates :commit_id, presence: true, if: :for_commit?
   validates :line_code, format: { with: /\A[a-z0-9]+_\d+_\d+\Z/ }, allow_blank: true
diff --git a/app/models/user.rb b/app/models/user.rb
index 67fef1c1e6a19b40409769f1950f704b35b7a5d7..719b49b16fe1e82fb61858cd24458586371f306d 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -54,6 +54,7 @@
 #  public_email               :string(255)      default(""), not null
 #  dashboard                  :integer          default(0)
 #  project_view               :integer          default(0)
+#  consumed_timestep          :integer
 #  layout                     :integer          default(0)
 #
 
@@ -388,33 +389,23 @@ class User < ActiveRecord::Base
     end
   end
 
-  # Groups user has access to
+  # Returns the groups a user has access to
   def authorized_groups
-    @authorized_groups ||= begin
-                             group_ids = (groups.pluck(:id) + authorized_projects.pluck(:namespace_id))
-                             Group.where(id: group_ids)
-                           end
-  end
+    union = Gitlab::SQL::Union.
+      new([groups.select(:id), authorized_projects.select(:namespace_id)])
 
-  def authorized_projects_id
-    @authorized_projects_id ||= begin
-      project_ids = personal_projects.pluck(:id)
-      project_ids.push(*groups_projects.pluck(:id))
-      project_ids.push(*projects.pluck(:id).uniq)
-    end
+    Group.where("namespaces.id IN (#{union.to_sql})")
   end
 
-  # Projects user has access to
+  # Returns the groups a user is authorized to access.
   def authorized_projects
-    @authorized_projects ||= Project.where(id: authorized_projects_id)
+    Project.where("projects.id IN (#{projects_union.to_sql})")
   end
 
   def owned_projects
     @owned_projects ||=
-      begin
-        namespace_ids = owned_groups.pluck(:id).push(namespace.id)
-        Project.in_namespace(namespace_ids).joins(:namespace)
-      end
+      Project.where('namespace_id IN (?) OR namespace_id = ?',
+                    owned_groups.select(:id), namespace.id).joins(:namespace)
   end
 
   # Team membership in authorized projects
@@ -646,11 +637,11 @@ class User < ActiveRecord::Base
     email.start_with?('temp-email-for-oauth')
   end
 
-  def avatar_url(size = nil)
+  def avatar_url(size = nil, scale = 2)
     if avatar.present?
       [gitlab_config.url, avatar.url].join
     else
-      GravatarService.new.execute(email, size)
+      GravatarService.new.execute(email, size, scale)
     end
   end
 
@@ -699,7 +690,7 @@ class User < ActiveRecord::Base
   end
 
   def starred?(project)
-    starred_projects.exists?(project)
+    starred_projects.exists?(project.id)
   end
 
   def toggle_star(project)
@@ -729,12 +720,25 @@ class User < ActiveRecord::Base
     Doorkeeper::AccessToken.where(resource_owner_id: self.id, revoked_at: nil)
   end
 
-  def contributed_projects_ids
-    Event.contributions.where(author_id: self).
+  # Returns the projects a user contributed to in the last year.
+  #
+  # This method relies on a subquery as this performs significantly better
+  # compared to a JOIN when coupled with, for example,
+  # `Project.visible_to_user`. That is, consider the following code:
+  #
+  #     some_user.contributed_projects.visible_to_user(other_user)
+  #
+  # If this method were to use a JOIN the resulting query would take roughly 200
+  # ms on a database with a similar size to GitLab.com's database. On the other
+  # hand, using a subquery means we can get the exact same data in about 40 ms.
+  def contributed_projects
+    events = Event.select(:project_id).
+      contributions.where(author_id: self).
       where("created_at > ?", Time.now - 1.year).
-      reorder(project_id: :desc).
-      select(:project_id).
-      uniq.map(&:project_id)
+      uniq.
+      reorder(nil)
+
+    Project.where(id: events)
   end
 
   def restricted_signup_domains
@@ -764,15 +768,35 @@ class User < ActiveRecord::Base
     !solo_owned_groups.present?
   end
 
-  def ci_authorized_projects
-    @ci_authorized_projects ||= Ci::Project.where(gitlab_id: authorized_projects_id)
-  end
-
   def ci_authorized_runners
     @ci_authorized_runners ||= begin
       runner_ids = Ci::RunnerProject.joins(:project).
-        where(ci_projects: { gitlab_id: authorized_projects_id }).select(:runner_id)
+        where("ci_projects.gitlab_id IN (#{ci_projects_union.to_sql})").
+        select(:runner_id)
+
       Ci::Runner.specific.where(id: runner_ids)
     end
   end
+
+  private
+
+  def projects_union
+    Gitlab::SQL::Union.new([personal_projects.select(:id),
+                            groups_projects.select(:id),
+                            projects.select(:id)])
+  end
+
+  def ci_projects_union
+    scope  = { access_level: [Gitlab::Access::MASTER, Gitlab::Access::OWNER] }
+    groups = groups_projects.where(members: scope)
+    other  = projects.where(members: scope)
+
+    Gitlab::SQL::Union.new([personal_projects.select(:id), groups.select(:id),
+                            other.select(:id)])
+  end
+
+  # Added according to https://github.com/plataformatec/devise/blob/7df57d5081f9884849ca15e4fde179ef164a575f/README.md#activejob-integration
+  def send_devise_notification(notification, *args)
+    devise_mailer.send(notification, self, *args).deliver_later
+  end
 end
diff --git a/app/models/users_star_project.rb b/app/models/users_star_project.rb
index 3d49cb059496e872ec63f7c312919d8e4d8659e4..413f3f485a85bed453588be0aebc870b083add16 100644
--- a/app/models/users_star_project.rb
+++ b/app/models/users_star_project.rb
@@ -10,7 +10,7 @@
 #
 
 class UsersStarProject < ActiveRecord::Base
-  belongs_to :project, counter_cache: :star_count
+  belongs_to :project, counter_cache: :star_count, touch: true
   belongs_to :user
 
   validates :user, presence: true
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index bfe6a3dc4be2c113cbe1bd5a86033df2379e2361..ec581658fc16d552cc0b4df2a1e26ea2034022d9 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@ require 'securerandom'
 # Compare 2 branches for one repo or between repositories
 # and return Gitlab::CompareResult object that responds to commits and diffs
 class CompareService
-  def execute(source_project, source_branch, target_project, target_branch)
+  def execute(source_project, source_branch, target_project, target_branch, diff_options = {})
     source_commit = source_project.commit(source_branch)
     return unless source_commit
 
@@ -25,7 +25,7 @@ class CompareService
         target_project.repository.raw_repository,
         target_branch,
         source_sha,
-      )
+      ), diff_options
     )
   end
 end
diff --git a/app/services/create_release_service.rb b/app/services/create_release_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e06a6f2f47a4d391a0bdc5ccfcf98e9fddb3f337
--- /dev/null
+++ b/app/services/create_release_service.rb
@@ -0,0 +1,31 @@
+require_relative 'base_service'
+
+class CreateReleaseService < BaseService
+  def execute(tag_name, release_description)
+
+    repository = project.repository
+    existing_tag = repository.find_tag(tag_name)
+
+    # Only create a release if the tag exists
+    if existing_tag
+      release = project.releases.find_by(tag: tag_name)
+
+      if release
+        error('Release already exists', 409)
+      else
+        release = project.releases.new({ tag: tag_name, description: release_description })
+        release.save
+
+        success(release)
+      end
+    else
+      error('Tag does not exist', 404)
+    end
+  end
+
+  def success(release)
+    out = super()
+    out[:release] = release
+    out
+  end
+end
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 9917119fce2499cca0eaa732421d8e15bd92051d..2452999382af08dcda44a9d8f0e957e12eb7e399 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -19,16 +19,16 @@ class CreateTagService < BaseService
     new_tag = repository.find_tag(tag_name)
 
     if new_tag
-      if release_description
-        release = project.releases.find_or_initialize_by(tag: tag_name)
-        release.update_attributes(description: release_description)
-      end
-
       push_data = create_push_data(project, current_user, new_tag)
       EventCreateService.new.push(project, current_user, push_data)
       project.execute_hooks(push_data.dup, :tag_push_hooks)
       project.execute_services(push_data.dup, :tag_push_hooks)
 
+      if release_description
+        CreateReleaseService.new(@project, @current_user).
+          execute(tag_name, release_description)
+      end
+
       success(new_tag)
     else
       error('Invalid reference name')
diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb
index 3de7bb9dcaaa80f2ef3b6f5baf00e10c0f573e0b..f11690aa3f465e1f84167202c86a0500af74c850 100644
--- a/app/services/git_push_service.rb
+++ b/app/services/git_push_service.rb
@@ -58,12 +58,6 @@ class GitPushService
 
     @push_data = build_push_data(oldrev, newrev, ref)
 
-    # If CI was disabled but .gitlab-ci.yml file was pushed
-    # we enable CI automatically
-    if !project.gitlab_ci? && gitlab_ci_yaml?(newrev)
-      project.enable_ci
-    end
-
     EventCreateService.new.push(project, user, @push_data)
     project.execute_hooks(@push_data.dup, :push_hooks)
     project.execute_services(@push_data.dup, :push_hooks)
@@ -134,10 +128,4 @@ class GitPushService
   def commit_user(commit)
     commit.author || user
   end
-
-  def gitlab_ci_yaml?(sha)
-    @project.repository.blob_at(sha, '.gitlab-ci.yml')
-  rescue Rugged::ReferenceError
-    nil
-  end
 end
diff --git a/app/services/gravatar_service.rb b/app/services/gravatar_service.rb
index 4bee0c26a68e1e7c32c2ea91946ac8ee0c79e65a..433ecc2df32a6a66759d772271a146c87a4218b8 100644
--- a/app/services/gravatar_service.rb
+++ b/app/services/gravatar_service.rb
@@ -1,13 +1,13 @@
 class GravatarService
   include Gitlab::CurrentSettings
 
-  def execute(email, size = nil)
+  def execute(email, size = nil, scale = 2)
     if current_application_settings.gravatar_enabled? && email.present?
       size = 40 if size.nil? || size <= 0
 
       sprintf gravatar_url,
         hash: Digest::MD5.hexdigest(email.strip.downcase),
-        size: size,
+        size: size * scale,
         email: email.strip
     end
   end
diff --git a/app/services/issuable_base_service.rb b/app/services/issuable_base_service.rb
index 15b3825f96a3fd48b1b8dcd625edcb3c74137f4a..2556f06e2d3afdd67aa1b7afbf8471e68e4ccac2 100644
--- a/app/services/issuable_base_service.rb
+++ b/app/services/issuable_base_service.rb
@@ -27,7 +27,16 @@ class IssuableBaseService < BaseService
       old_branch, new_branch)
   end
 
+  def create_task_status_note(issuable)
+    issuable.updated_tasks.each do |task|
+      SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
+    end
+  end
+
   def filter_params(issuable_ability_name = :issue)
+    params[:assignee_id]  = "" if params[:assignee_id] == IssuableFinder::NONE
+    params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
+
     ability = :"admin_#{issuable_ability_name}"
 
     unless can?(current_user, ability, project)
@@ -36,4 +45,44 @@ class IssuableBaseService < BaseService
       params.delete(:assignee_id)
     end
   end
+
+  def update(issuable)
+    change_state(issuable)
+    filter_params
+    old_labels = issuable.labels.to_a
+
+    if params.present? && issuable.update_attributes(params.merge(updated_by: current_user))
+      issuable.reset_events_cache
+      handle_common_system_notes(issuable, old_labels: old_labels)
+      handle_changes(issuable)
+      issuable.create_new_cross_references!(current_user)
+      execute_hooks(issuable, 'update')
+    end
+
+    issuable
+  end
+
+  def change_state(issuable)
+    case params.delete(:state_event)
+    when 'reopen'
+      reopen_service.new(project, current_user, {}).execute(issuable)
+    when 'close'
+      close_service.new(project, current_user, {}).execute(issuable)
+    end
+  end
+
+  def handle_common_system_notes(issuable, options = {})
+    if issuable.previous_changes.include?('title')
+      create_title_change_note(issuable, issuable.previous_changes['title'].first)
+    end
+
+    if issuable.previous_changes.include?('description') && issuable.tasks?
+      create_task_status_note(issuable)
+    end
+
+    old_labels = options[:old_labels]
+    if old_labels && (issuable.labels != old_labels)
+      create_labels_note(issuable, issuable.labels - old_labels, old_labels - issuable.labels)
+    end
+  end
 end
diff --git a/app/services/issues/update_service.rb b/app/services/issues/update_service.rb
index 551325e4cab43d4ed9bee2630b348b267aa523fb..a55a04dd5e07844346e5efebf02c13aafff06372 100644
--- a/app/services/issues/update_service.rb
+++ b/app/services/issues/update_service.rb
@@ -1,45 +1,26 @@
 module Issues
   class UpdateService < Issues::BaseService
     def execute(issue)
-      case params.delete(:state_event)
-      when 'reopen'
-        Issues::ReopenService.new(project, current_user, {}).execute(issue)
-      when 'close'
-        Issues::CloseService.new(project, current_user, {}).execute(issue)
-      end
-
-      params[:assignee_id]  = "" if params[:assignee_id] == IssuableFinder::NONE
-      params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
-
-      filter_params
-      old_labels = issue.labels.to_a
-
-      if params.present? && issue.update_attributes(params.merge(updated_by: current_user))
-        issue.reset_events_cache
-
-        if issue.labels != old_labels
-          create_labels_note(
-            issue, issue.labels - old_labels, old_labels - issue.labels)
-        end
-
-        if issue.previous_changes.include?('milestone_id')
-          create_milestone_note(issue)
-        end
-
-        if issue.previous_changes.include?('assignee_id')
-          create_assignee_note(issue)
-          notification_service.reassigned_issue(issue, current_user)
-        end
+      update(issue)
+    end
 
-        if issue.previous_changes.include?('title')
-          create_title_change_note(issue, issue.previous_changes['title'].first)
-        end
+    def handle_changes(issue)
+      if issue.previous_changes.include?('milestone_id')
+        create_milestone_note(issue)
+      end
 
-        issue.create_new_cross_references!(current_user)
-        execute_hooks(issue, 'update')
+      if issue.previous_changes.include?('assignee_id')
+        create_assignee_note(issue)
+        notification_service.reassigned_issue(issue, current_user)
       end
+    end
+
+    def reopen_service
+      Issues::ReopenService
+    end
 
-      issue
+    def close_service
+      Issues::CloseService
     end
   end
 end
diff --git a/app/services/labels/group_service.rb b/app/services/labels/group_service.rb
deleted file mode 100644
index b26cee24d56bbd1ab330744e74cc0b558a2f6fc8..0000000000000000000000000000000000000000
--- a/app/services/labels/group_service.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Labels
-  class GroupService < ::BaseService
-    def initialize(project_labels)
-      @project_labels = project_labels.group_by(&:title)
-    end
-
-    def execute
-      build(@project_labels)
-    end
-
-    def label(title)
-      if title
-        group_label = @project_labels[title].group_by(&:title)
-        build(group_label).first
-      else
-        nil
-      end
-    end
-
-    private
-
-    def build(label)
-      label.map { |title, labels| GroupLabel.new(title, labels) }
-    end
-  end
-end
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index 7963af127e1c61c92fc5361518fef0e4dd7c43bb..d619b72e3c28db32ee7f0e1fad162d535dd2d2f8 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -38,7 +38,7 @@ module MergeRequests
       }
 
       repository.merge(current_user, merge_request.source_sha, merge_request.target_branch, options)
-    rescue Exception => e
+    rescue StandardError => e
       merge_request.update(merge_error: "Something went wrong during merge")
       Rails.logger.error(e.message)
       return false
diff --git a/app/services/merge_requests/update_service.rb b/app/services/merge_requests/update_service.rb
index 61f7d2bbe895fc3d197c46a2b23320bb6aca2ce9..5ff2cc03ddafb024b7bd08fd577ee6d6bc5a49ad 100644
--- a/app/services/merge_requests/update_service.rb
+++ b/app/services/merge_requests/update_service.rb
@@ -11,59 +11,37 @@ module MergeRequests
       params.except!(:target_project_id)
       params.except!(:source_branch)
 
-      case params.delete(:state_event)
-      when 'reopen'
-        MergeRequests::ReopenService.new(project, current_user, {}).execute(merge_request)
-      when 'close'
-        MergeRequests::CloseService.new(project, current_user, {}).execute(merge_request)
-      end
-
-      params[:assignee_id]  = "" if params[:assignee_id] == IssuableFinder::NONE
-      params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
-
-      filter_params
-      old_labels = merge_request.labels.to_a
-
-      if params.present? && merge_request.update_attributes(params.merge(updated_by: current_user))
-        merge_request.reset_events_cache
-
-        if merge_request.labels != old_labels
-          create_labels_note(
-            merge_request,
-            merge_request.labels - old_labels,
-            old_labels - merge_request.labels
-          )
-        end
-
-        if merge_request.previous_changes.include?('target_branch')
-          create_branch_change_note(merge_request, 'target',
-                                    merge_request.previous_changes['target_branch'].first,
-                                    merge_request.target_branch)
-        end
-
-        if merge_request.previous_changes.include?('milestone_id')
-          create_milestone_note(merge_request)
-        end
+      update(merge_request)
+    end
 
-        if merge_request.previous_changes.include?('assignee_id')
-          create_assignee_note(merge_request)
-          notification_service.reassigned_merge_request(merge_request, current_user)
-        end
+    def handle_changes(merge_request)
+      if merge_request.previous_changes.include?('target_branch')
+        create_branch_change_note(merge_request, 'target',
+                                  merge_request.previous_changes['target_branch'].first,
+                                  merge_request.target_branch)
+      end
 
-        if merge_request.previous_changes.include?('title')
-          create_title_change_note(merge_request, merge_request.previous_changes['title'].first)
-        end
+      if merge_request.previous_changes.include?('milestone_id')
+        create_milestone_note(merge_request)
+      end
 
-        if merge_request.previous_changes.include?('target_branch') ||
-            merge_request.previous_changes.include?('source_branch')
-          merge_request.mark_as_unchecked
-        end
+      if merge_request.previous_changes.include?('assignee_id')
+        create_assignee_note(merge_request)
+        notification_service.reassigned_merge_request(merge_request, current_user)
+      end
 
-        merge_request.create_new_cross_references!(current_user)
-        execute_hooks(merge_request, 'update')
+      if merge_request.previous_changes.include?('target_branch') ||
+          merge_request.previous_changes.include?('source_branch')
+        merge_request.mark_as_unchecked
       end
+    end
+
+    def reopen_service
+      MergeRequests::ReopenService
+    end
 
-      merge_request
+    def close_service
+      MergeRequests::CloseService
     end
   end
 end
diff --git a/app/services/milestones/group_service.rb b/app/services/milestones/group_service.rb
deleted file mode 100644
index 11d702f1e7b33753f1ccc4e14294aee7e3b21a15..0000000000000000000000000000000000000000
--- a/app/services/milestones/group_service.rb
+++ /dev/null
@@ -1,26 +0,0 @@
-module Milestones
-  class GroupService < Milestones::BaseService
-    def initialize(project_milestones)
-      @project_milestones = project_milestones.group_by(&:title)
-    end
-
-    def execute
-      build(@project_milestones)
-    end
-
-    def milestone(title)
-      if title
-        group_milestone = @project_milestones[title].group_by(&:title)
-        build(group_milestone).first
-      else
-        nil
-      end
-    end
-
-    private
-
-    def build(milestone)
-      milestone.map{ |title, milestones| GroupMilestone.new(title, milestones) }
-    end
-  end
-end
diff --git a/app/services/notes/create_service.rb b/app/services/notes/create_service.rb
index 2001dc89c3332fbbf91770d2dc31aefb7d244eb9..dbff58dfb9c78564d7e20ddb5613c4eafe93effd 100644
--- a/app/services/notes/create_service.rb
+++ b/app/services/notes/create_service.rb
@@ -5,11 +5,16 @@ module Notes
       note.author = current_user
       note.system = false
 
+      if contains_emoji_only?(params[:note])
+        note.is_award = true
+        note.note = emoji_name(params[:note])
+      end
+
       if note.save
         notification_service.new_note(note)
 
-        # Skip system notes, like status changes and cross-references.
-        unless note.system
+        # Skip system notes, like status changes and cross-references and awards
+        unless note.system || note.is_award
           event_service.leave_note(note, note.author)
           note.create_cross_references!
           execute_hooks(note)
@@ -28,5 +33,13 @@ module Notes
       note.project.execute_hooks(note_data, :note_hooks)
       note.project.execute_services(note_data, :note_hooks)
     end
+
+    def contains_emoji_only?(note)
+      note =~ /\A:[-_+[:alnum:]]*:\s?\z/
+    end
+
+    def emoji_name(note)
+      note.match(/\A:([-_+[:alnum:]]*):\s?/)[1]
+    end
   end
 end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index a6b2234865047a8470101013749730185ad81454..388a4defb2632d9349b931abe1a39b7912ee3328 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -13,14 +13,14 @@ class NotificationService
   # even if user disabled notifications
   def new_key(key)
     if key.user
-      mailer.new_ssh_key_email(key.id)
+      mailer.new_ssh_key_email(key.id).deliver_later
     end
   end
 
   # Always notify user about email added to profile
   def new_email(email)
     if email.user
-      mailer.new_email_email(email.id)
+      mailer.new_email_email(email.id).deliver_later
     end
   end
 
@@ -79,17 +79,27 @@ class NotificationService
   end
 
   def merge_mr(merge_request, current_user)
-    close_resource_email(merge_request, merge_request.target_project, current_user, 'merged_merge_request_email')
+    close_resource_email(
+      merge_request,
+      merge_request.target_project,
+      current_user,
+      'merged_merge_request_email'
+    )
   end
 
   def reopen_mr(merge_request, current_user)
-    reopen_resource_email(merge_request, merge_request.target_project, current_user, 'merge_request_status_email', 'reopened')
+    reopen_resource_email(
+      merge_request,
+      merge_request.target_project,
+      current_user, 'merge_request_status_email',
+      'reopened'
+    )
   end
 
   # Notify new user with email after creation
   def new_user(user, token = nil)
     # Don't email omniauth created users
-    mailer.new_user_email(user.id, token) unless user.identities.any?
+    mailer.new_user_email(user.id, token).deliver_later unless user.identities.any?
   end
 
   # Notify users on new note in system
@@ -102,6 +112,7 @@ class NotificationService
     # ignore gitlab service messages
     return true if note.note.start_with?('Status changed to closed')
     return true if note.cross_reference? && note.system == true
+    return true if note.is_award
 
     target = note.noteable
 
@@ -113,7 +124,7 @@ class NotificationService
     end
 
     # Add all users participating in the thread (author, assignee, comment authors)
-    participants = 
+    participants =
       if target.respond_to?(:participants)
         target.participants(note.author)
       else
@@ -137,50 +148,59 @@ class NotificationService
 
     # build notify method like 'note_commit_email'
     notify_method = "note_#{note.noteable_type.underscore}_email".to_sym
-
     recipients.each do |recipient|
-      mailer.send(notify_method, recipient.id, note.id)
+      mailer.send(notify_method, recipient.id, note.id).deliver_later
     end
   end
 
   def invite_project_member(project_member, token)
-    mailer.project_member_invited_email(project_member.id, token)
+    mailer.project_member_invited_email(project_member.id, token).deliver_later
   end
 
   def accept_project_invite(project_member)
-    mailer.project_invite_accepted_email(project_member.id)
+    mailer.project_invite_accepted_email(project_member.id).deliver_later
   end
 
   def decline_project_invite(project_member)
-    mailer.project_invite_declined_email(project_member.project.id, project_member.invite_email, project_member.access_level, project_member.created_by_id)
+    mailer.project_invite_declined_email(
+      project_member.project.id,
+      project_member.invite_email,
+      project_member.access_level,
+      project_member.created_by_id
+    ).deliver_later
   end
 
   def new_project_member(project_member)
-    mailer.project_access_granted_email(project_member.id)
+    mailer.project_access_granted_email(project_member.id).deliver_later
   end
 
   def update_project_member(project_member)
-    mailer.project_access_granted_email(project_member.id)
+    mailer.project_access_granted_email(project_member.id).deliver_later
   end
 
   def invite_group_member(group_member, token)
-    mailer.group_member_invited_email(group_member.id, token)
+    mailer.group_member_invited_email(group_member.id, token).deliver_later
   end
 
   def accept_group_invite(group_member)
-    mailer.group_invite_accepted_email(group_member.id)
+    mailer.group_invite_accepted_email(group_member.id).deliver_later
   end
 
   def decline_group_invite(group_member)
-    mailer.group_invite_declined_email(group_member.group.id, group_member.invite_email, group_member.access_level, group_member.created_by_id)
+    mailer.group_invite_declined_email(
+      group_member.group.id,
+      group_member.invite_email,
+      group_member.access_level,
+      group_member.created_by_id
+    ).deliver_later
   end
 
   def new_group_member(group_member)
-    mailer.group_access_granted_email(group_member.id)
+    mailer.group_access_granted_email(group_member.id).deliver_later
   end
 
   def update_group_member(group_member)
-    mailer.group_access_granted_email(group_member.id)
+    mailer.group_access_granted_email(group_member.id).deliver_later
   end
 
   def project_was_moved(project, old_path_with_namespace)
@@ -188,7 +208,11 @@ class NotificationService
     recipients = reject_muted_users(recipients, project)
 
     recipients.each do |recipient|
-      mailer.project_was_moved_email(project.id, recipient.id, old_path_with_namespace)
+      mailer.project_was_moved_email(
+        project.id,
+        recipient.id,
+        old_path_with_namespace
+      ).deliver_later
     end
   end
 
@@ -276,35 +300,25 @@ class NotificationService
   # Remove users with disabled notifications from array
   # Also remove duplications and nil recipients
   def reject_muted_users(users, project = nil)
-    users = users.to_a.compact.uniq
-    users = users.reject(&:blocked?)
-
-    users.reject do |user|
-      next user.notification.disabled? unless project
-
-      member = project.project_members.find_by(user_id: user.id)
-
-      if !member && project.group
-        member = project.group.group_members.find_by(user_id: user.id)
-      end
-
-      # reject users who globally disabled notification and has no membership
-      next user.notification.disabled? unless member
-
-      # reject users who disabled notification in project
-      next true if member.notification.disabled?
-
-      # reject users who have N_GLOBAL in project and disabled in global settings
-      member.notification.global? && user.notification.disabled?
-    end
+    reject_users(users, :disabled?, project)
   end
 
   # Remove users with notification level 'Mentioned'
   def reject_mention_users(users, project = nil)
+    reject_users(users, :mention?, project)
+  end
+
+  # Reject users which method_name from notification object returns true.
+  #
+  # Example:
+  #   reject_users(users, :watch?, project)
+  #
+  def reject_users(users, method_name, project = nil)
     users = users.to_a.compact.uniq
+    users = users.reject(&:blocked?)
 
     users.reject do |user|
-      next user.notification.mention? unless project
+      next user.notification.send(method_name) unless project
 
       member = project.project_members.find_by(user_id: user.id)
 
@@ -313,19 +327,19 @@ class NotificationService
       end
 
       # reject users who globally set mention notification and has no membership
-      next user.notification.mention? unless member
+      next user.notification.send(method_name) unless member
 
       # reject users who set mention notification in project
-      next true if member.notification.mention?
+      next true if member.notification.send(method_name)
 
       # reject users who have N_MENTION in project and disabled in global settings
-      member.notification.global? && user.notification.mention?
+      member.notification.global? && user.notification.send(method_name)
     end
   end
 
   def reject_unsubscribed_users(recipients, target)
     return recipients unless target.respond_to? :subscriptions
-    
+
     recipients.reject do |user|
       subscription = target.subscriptions.find_by_user_id(user.id)
       subscription && !subscription.subscribed
@@ -343,12 +357,12 @@ class NotificationService
       recipients
     end
   end
-  
+
   def new_resource_email(target, project, method)
     recipients = build_recipients(target, project, target.author)
 
     recipients.each do |recipient|
-      mailer.send(method, recipient.id, target.id)
+      mailer.send(method, recipient.id, target.id).deliver_later
     end
   end
 
@@ -356,16 +370,24 @@ class NotificationService
     recipients = build_recipients(target, project, current_user)
 
     recipients.each do |recipient|
-      mailer.send(method, recipient.id, target.id, current_user.id)
+      mailer.send(method, recipient.id, target.id, current_user.id).deliver_later
     end
   end
 
   def reassign_resource_email(target, project, current_user, method)
-    assignee_id_was = previous_record(target, "assignee_id")
-    recipients = build_recipients(target, project, current_user)
+    previous_assignee_id = previous_record(target, "assignee_id")
+    previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
+
+    recipients = build_recipients(target, project, current_user, [previous_assignee])
 
     recipients.each do |recipient|
-      mailer.send(method, recipient.id, target.id, assignee_id_was, current_user.id)
+      mailer.send(
+        method,
+        recipient.id,
+        target.id,
+        previous_assignee_id,
+        current_user.id
+      ).deliver_later
     end
   end
 
@@ -373,13 +395,15 @@ class NotificationService
     recipients = build_recipients(target, project, current_user)
 
     recipients.each do |recipient|
-      mailer.send(method, recipient.id, target.id, status, current_user.id)
+      mailer.send(method, recipient.id, target.id, status, current_user.id).deliver_later
     end
   end
 
-  def build_recipients(target, project, current_user)
+  def build_recipients(target, project, current_user, extra_recipients = nil)
     recipients = target.participants(current_user)
 
+    recipients = recipients.concat(extra_recipients).compact.uniq if extra_recipients
+
     recipients = add_project_watchers(recipients, project)
     recipients = reject_mention_users(recipients, project)
     recipients = reject_muted_users(recipients, project)
@@ -393,7 +417,7 @@ class NotificationService
   end
 
   def mailer
-    Notify.delay
+    Notify
   end
 
   def previous_record(object, attribute)
diff --git a/app/services/projects/create_service.rb b/app/services/projects/create_service.rb
index 5b84527eccfcb6fb9b40475c8c6782090494552c..700a1db04d83ecf78f19fa5e1b0021eefc392be7 100644
--- a/app/services/projects/create_service.rb
+++ b/app/services/projects/create_service.rb
@@ -55,7 +55,9 @@ module Projects
         @project.save
 
         if @project.persisted? && !@project.import?
-          raise 'Failed to create repository' unless @project.create_repository
+          unless @project.create_repository
+            raise 'Failed to create repository'
+          end
         end
       end
 
@@ -94,9 +96,7 @@ module Projects
         @project.team << [current_user, :master, current_user]
       end
 
-      if @project.import?
-        @project.import_start
-      end
+      @project.import_start if @project.import?
     end
   end
 end
diff --git a/app/services/projects/fork_service.rb b/app/services/projects/fork_service.rb
index 46374a3909a8bd3e7b3165dcb51ef78b174d3d20..5da1c7afd924dfcf19f106320987dfa1f0257b39 100644
--- a/app/services/projects/fork_service.rb
+++ b/app/services/projects/fork_service.rb
@@ -17,7 +17,7 @@ module Projects
       new_project = CreateService.new(current_user, new_params).execute
 
       if new_project.persisted?
-        if @project.gitlab_ci?
+        if @project.builds_enabled?
           new_project.enable_ci
 
           settings = @project.gitlab_ci_project.attributes.select do |attr_name, value|
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 9a5fe4af9dde7e93b9c298018867b9a52309c15f..8b5143e1eb75a45920bed06dc8ec68e82fd4faab 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -33,17 +33,7 @@ class SystemHooksService
         )
       end
     when Project
-      owner = model.owner
-
-      data.merge!({
-        name: model.name,
-        path: model.path,
-        path_with_namespace: model.path_with_namespace,
-        project_id: model.id,
-        owner_name: owner.name,
-        owner_email: owner.respond_to?(:email) ?  owner.email : "",
-        project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
-      })
+      data.merge!(project_data(model))
     when User
       data.merge!({
         name: model.name,
@@ -51,16 +41,7 @@ class SystemHooksService
         user_id: model.id
       })
     when ProjectMember
-      data.merge!({
-        project_name: model.project.name,
-        project_path: model.project.path,
-        project_path_with_namespace: model.project.path_with_namespace,
-        project_id: model.project.id,
-        user_name: model.user.name,
-        user_email: model.user.email,
-        access_level: model.human_access,
-        project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
-      })
+      data.merge!(project_member_data(model))
     when Group
       owner = model.owner
 
@@ -72,15 +53,7 @@ class SystemHooksService
         owner_email: owner.respond_to?(:email) ? owner.email : nil,
       )
     when GroupMember
-      data.merge!(
-        group_name: model.group.name,
-        group_path: model.group.path,
-        group_id: model.group.id,
-        user_name: model.user.name,
-        user_email: model.user.email,
-        user_id: model.user.id,
-        group_access: model.human_access,
-      )
+      data.merge!(group_member_data(model))
     end
   end
 
@@ -96,4 +69,43 @@ class SystemHooksService
       "#{model.class.name.downcase}_#{event.to_s}"
     end
   end
+
+  def project_data(model)
+    owner = model.owner
+
+    {
+      name: model.name,
+      path: model.path,
+      path_with_namespace: model.path_with_namespace,
+      project_id: model.id,
+      owner_name: owner.name,
+      owner_email: owner.respond_to?(:email) ?  owner.email : "",
+      project_visibility: Project.visibility_levels.key(model.visibility_level_field).downcase
+    }
+  end
+
+  def project_member_data(model)
+    {
+      project_name: model.project.name,
+      project_path: model.project.path,
+      project_path_with_namespace: model.project.path_with_namespace,
+      project_id: model.project.id,
+      user_name: model.user.name,
+      user_email: model.user.email,
+      access_level: model.human_access,
+      project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
+    }
+  end
+
+  def group_member_data(model)
+    {
+      group_name: model.group.name,
+      group_path: model.group.path,
+      group_id: model.group.id,
+      user_name: model.user.name,
+      user_email: model.user.email,
+      user_id: model.user.id,
+      group_access: model.human_access,
+    }
+  end
 end
diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb
index 708c2f00486aef8ca0d54aacee44fcc0d166beab..7e2bc834176a7b832ccf183a4f03cfa09253f074 100644
--- a/app/services/system_note_service.rb
+++ b/app/services/system_note_service.rb
@@ -341,4 +341,22 @@ class SystemNoteService
 
     "* #{commit_ids} - #{commits_text} from branch `#{branch}`\n"
   end
+
+  # Called when the status of a Task has changed
+  #
+  # noteable  - Noteable object.
+  # project   - Project owning noteable
+  # author    - User performing the change
+  # new_task  - TaskList::Item object.
+  #
+  # Example Note text:
+  #
+  #   "Soandso marked the task Whatever as completed."
+  #
+  # Returns the created Note object
+  def self.change_task_status(noteable, project, author, new_task)
+    status_label = new_task.complete? ? Taskable::COMPLETED : Taskable::INCOMPLETE
+    body = "Marked the task **#{new_task.source}** as #{status_label}"
+    create_note(noteable: noteable, project: project, author: author, note: body)
+  end
 end
diff --git a/app/services/update_release_service.rb b/app/services/update_release_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25eb13ef09a4312a95ca0b7f1dd86bb1451d3f67
--- /dev/null
+++ b/app/services/update_release_service.rb
@@ -0,0 +1,29 @@
+require_relative 'base_service'
+
+class UpdateReleaseService < BaseService
+  def execute(tag_name, release_description)
+
+    repository = project.repository
+    existing_tag = repository.find_tag(tag_name)
+
+    if existing_tag
+      release = project.releases.find_by(tag: tag_name)
+
+      if release
+        release.update_attributes(description: release_description)
+
+        success(release)
+      else
+        error('Release does not exist', 404)
+      end
+    else
+      error('Tag does not exist', 404)
+    end
+  end
+
+  def success(release)
+    out = super()
+    out[:release] = release
+    out
+  end
+end
diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
index b4e0fc5772dc101fdd760c3e1a95d6ec96cf6aca..1dccc39e7e2e4d5902948453b71e19aab275866c 100644
--- a/app/uploaders/artifact_uploader.rb
+++ b/app/uploaders/artifact_uploader.rb
@@ -5,15 +5,15 @@ class ArtifactUploader < CarrierWave::Uploader::Base
   attr_accessor :build, :field
 
   def self.artifacts_path
-    File.expand_path('shared/artifacts/', Rails.root)
+    Gitlab.config.artifacts.path
   end
 
   def self.artifacts_upload_path
-    File.expand_path('shared/artifacts/tmp/uploads/', Rails.root)
+    File.join(self.artifacts_path, 'tmp/uploads/')
   end
 
   def self.artifacts_cache_path
-    File.expand_path('shared/artifacts/tmp/cache/', Rails.root)
+    File.join(self.artifacts_path, 'tmp/cache/')
   end
 
   def initialize(build, field)
diff --git a/app/uploaders/attachment_uploader.rb b/app/uploaders/attachment_uploader.rb
index a9691bee46e23f20eca66b96e0577b700769b0e1..a65a896e41e597394003888e50d217c7a5dfc9d8 100644
--- a/app/uploaders/attachment_uploader.rb
+++ b/app/uploaders/attachment_uploader.rb
@@ -1,26 +1,11 @@
 # encoding: utf-8
 
 class AttachmentUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   def store_dir
     "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
   end
-
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
 end
diff --git a/app/uploaders/avatar_uploader.rb b/app/uploaders/avatar_uploader.rb
index 7cad044555b1aeae2eacba6b7eb57386fdf565b8..6135c3ad96f4e5c3916f426d08cc177880829a2a 100644
--- a/app/uploaders/avatar_uploader.rb
+++ b/app/uploaders/avatar_uploader.rb
@@ -1,6 +1,8 @@
 # encoding: utf-8
 
 class AvatarUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   after :store, :reset_events_cache
@@ -9,23 +11,6 @@ class AvatarUploader < CarrierWave::Uploader::Base
     "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
   end
 
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
-
   def reset_events_cache(file)
     model.reset_events_cache if model.is_a?(User)
   end
diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb
index e82115858342f4af270fa2d474753a52ed2f2829..ac920119a854fc2b4a973579bffc5733193514a6 100644
--- a/app/uploaders/file_uploader.rb
+++ b/app/uploaders/file_uploader.rb
@@ -1,5 +1,7 @@
 # encoding: utf-8
 class FileUploader < CarrierWave::Uploader::Base
+  include UploaderHelper
+
   storage :file
 
   attr_accessor :project, :secret
@@ -28,21 +30,4 @@ class FileUploader < CarrierWave::Uploader::Base
   def secure_url
     File.join("/uploads", @secret, file.filename)
   end
-
-  def file_storage?
-    self.class.storage == CarrierWave::Storage::File
-  end
-
-  def image?
-    img_ext = %w(png jpg jpeg gif bmp tiff)
-    if file.respond_to?(:extension)
-      img_ext.include?(file.extension.downcase)
-    else
-      # Not all CarrierWave storages respond to :extension
-      ext = file.path.split('.').last.downcase
-      img_ext.include?(ext)
-    end
-  rescue
-    false
-  end
 end
diff --git a/app/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb
new file mode 100644
index 0000000000000000000000000000000000000000..28085b310837712ae030988964280158950e0dee
--- /dev/null
+++ b/app/uploaders/lfs_object_uploader.rb
@@ -0,0 +1,29 @@
+# encoding: utf-8
+
+class LfsObjectUploader < CarrierWave::Uploader::Base
+  storage :file
+
+  def store_dir
+    "#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}"
+  end
+
+  def cache_dir
+    "#{Gitlab.config.lfs.storage_path}/tmp/cache"
+  end
+
+  def move_to_cache
+    true
+  end
+
+  def move_to_store
+    true
+  end
+
+  def exists?
+    file.try(:exists?)
+  end
+
+  def filename
+    model.oid[4..-1]
+  end
+end
diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5ef440f3367c3c4f12c2577cd84171f5f1964677
--- /dev/null
+++ b/app/uploaders/uploader_helper.rb
@@ -0,0 +1,19 @@
+# Extra methods for uploader
+module UploaderHelper
+  def image?
+    img_ext = %w(png jpg jpeg gif bmp tiff)
+    if file.respond_to?(:extension)
+      img_ext.include?(file.extension.downcase)
+    else
+      # Not all CarrierWave storages respond to :extension
+      ext = file.path.split('.').last.downcase
+      img_ext.include?(ext)
+    end
+  rescue
+    false
+  end
+
+  def file_storage?
+    self.class.storage == CarrierWave::Storage::File
+  end
+end
diff --git a/app/views/admin/labels/_label.html.haml b/app/views/admin/labels/_label.html.haml
index 596e06243ddc6a7f9c8fe5691da45a27341e95a7..e3ccbf6c3a84e6d7896b386dfe3c7a482c7cab67 100644
--- a/app/views/admin/labels/_label.html.haml
+++ b/app/views/admin/labels/_label.html.haml
@@ -2,4 +2,4 @@
   = render_colored_label(label)
   .pull-right
     = link_to 'Edit', edit_admin_label_path(label), class: 'btn btn-sm'
-    = link_to 'Remove', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
+    = link_to 'Delete', admin_label_path(label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Delete this label? Are you sure?"}
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 4245d0f1eda1f52b3c84c7f830493af0acc87775..5e17b018163d05f47b86f658b3aa50b0aa7993e2 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -6,8 +6,8 @@
     %span.cred (Admin)
 
   .pull-right
-    - unless @user == current_user
-      = link_to 'Log in as this user', login_as_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
+    - unless @user == current_user || @user.blocked?
+      = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-grouped btn-info"
     = link_to edit_admin_user_path(@user), class: "btn btn-grouped" do
       %i.fa.fa-pencil-square-o
       Edit
diff --git a/app/views/admin/users/_profile.html.haml b/app/views/admin/users/_profile.html.haml
index 90d9980c85ca44e9a127a9378f6b7279d5e22694..7d11edc79e25147e6011f02706d36a002a58a377 100644
--- a/app/views/admin/users/_profile.html.haml
+++ b/app/views/admin/users/_profile.html.haml
@@ -16,11 +16,11 @@
     - unless user.linkedin.blank?
       %li
         %span.light LinkedIn:
-        %strong= link_to user.linkedin, "http://www.linkedin.com/in/#{user.linkedin}"
+        %strong= link_to user.linkedin, "https://www.linkedin.com/in/#{user.linkedin}"
     - unless user.twitter.blank?
       %li
         %span.light Twitter:
-        %strong= link_to user.twitter, "http://www.twitter.com/#{user.twitter}"
+        %strong= link_to user.twitter, "https://twitter.com/#{user.twitter}"
     - unless user.website_url.blank?
       %li
         %span.light Website:
diff --git a/app/views/admin/users/_projects.html.haml b/app/views/admin/users/_projects.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..a126a858ea85f0ce96e785dc2ebc0297a161d38f
--- /dev/null
+++ b/app/views/admin/users/_projects.html.haml
@@ -0,0 +1,13 @@
+- if local_assigns.has_key?(:contributed_projects) && contributed_projects.present?
+  .panel.panel-default.contributed-projects
+    .panel-heading Projects contributed to
+    = render 'shared/projects/list',
+      projects: contributed_projects.sort_by(&:star_count).reverse,
+      projects_limit: 5, stars: true, avatar: false
+
+- if local_assigns.has_key?(:projects) && projects.present?
+  .panel.panel-default
+    .panel-heading Personal projects
+    = render 'shared/projects/list',
+      projects: projects.sort_by(&:star_count).reverse,
+      projects_limit: 10, stars: true, avatar: false
diff --git a/app/views/admin/users/projects.html.haml b/app/views/admin/users/projects.html.haml
index 0d7a1a25a8043bef5bb20c2a06ce338c19c5ab46..b655b2a15f5b123dcec41050ee77926de734172b 100644
--- a/app/views/admin/users/projects.html.haml
+++ b/app/views/admin/users/projects.html.haml
@@ -14,7 +14,7 @@
 .row
   .col-md-6
     - if @personal_projects.present?
-      = render 'users/projects', projects: @personal_projects
+      = render 'admin/users/projects', projects: @personal_projects
     - else
       .nothing-here-block This user has no personal projects.
 
diff --git a/app/views/ci/admin/runners/show.html.haml b/app/views/ci/admin/runners/show.html.haml
index 1498db46a80ac044fed4a4b2f8c5d9a06e890d25..fd3d33d657b8efe6a2aaa536d560e88a39951fd3 100644
--- a/app/views/ci/admin/runners/show.html.haml
+++ b/app/views/ci/admin/runners/show.html.haml
@@ -37,7 +37,7 @@
     = label_tag :tag_list, class: 'control-label' do
       Tags
     .col-sm-10
-      = f.text_field :tag_list, class: 'form-control'
+      = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
       .help-block You can setup builds to only use runners with specific tags
   .form-actions
     = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/ci/notify/build_fail_email.html.haml b/app/views/ci/notify/build_fail_email.html.haml
index b0aaea89075b1e29ce5a707e40e294c667d8d3b3..de6291aa91484c409e47351eca9c2fa10cf0b04d 100644
--- a/app/views/ci/notify/build_fail_email.html.haml
+++ b/app/views/ci/notify/build_fail_email.html.haml
@@ -7,7 +7,7 @@
     = @project.name
 
 %p
-  Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)}
+  Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)}
 %p
   Author: #{@build.commit.git_author_name}
 %p
diff --git a/app/views/ci/notify/build_success_email.html.haml b/app/views/ci/notify/build_success_email.html.haml
index 24c439e50eb39fa3a0a79fbaebe8ba8bf3b2dc03..6ef1fd67d894792c33d8315e63ffbbe0e900881a 100644
--- a/app/views/ci/notify/build_success_email.html.haml
+++ b/app/views/ci/notify/build_success_email.html.haml
@@ -8,7 +8,7 @@
     = @project.name
 
 %p
-  Commit: #{link_to @build.short_sha, namespace_project_commit_path(@build.gl_project.namespace, @build.gl_project, @build.sha)}
+  Commit: #{link_to @build.short_sha, namespace_project_commit_url(@build.gl_project.namespace, @build.gl_project, @build.sha)}
 %p
   Author: #{@build.commit.git_author_name}
 %p
diff --git a/app/views/dashboard/groups/index.html.haml b/app/views/dashboard/groups/index.html.haml
index f3f3f58111e3cf8d54bdd4e3b90ed6d74e405679..d5b7e729e7bc026569061948d399779df257a23e 100644
--- a/app/views/dashboard/groups/index.html.haml
+++ b/app/views/dashboard/groups/index.html.haml
@@ -8,8 +8,8 @@
       = link_to new_group_path, class: "btn btn-new" do
         %i.fa.fa-plus
         New Group
-  .title Welcome to the groups!
-  Group members have access to all group projects.
+  .oneline
+    Group members have access to all group projects.
 
 %ul.content-list
   - @group_members.each do |group_member|
diff --git a/app/views/dashboard/milestones/index.html.haml b/app/views/dashboard/milestones/index.html.haml
index 21b25c3986e8a3903f1b0e8c3b0568b71cfdc162..635251e2374e82ceed3bc9b4a31b80489a9fe4b8 100644
--- a/app/views/dashboard/milestones/index.html.haml
+++ b/app/views/dashboard/milestones/index.html.haml
@@ -10,10 +10,10 @@
 
 .milestones
   %ul.content-list
-    - if @dashboard_milestones.blank?
+    - if @milestones.blank?
       %li
         .nothing-here-block No milestones to show
     - else
-      - @dashboard_milestones.each do |milestone|
+      - @milestones.each do |milestone|
         = render 'milestone', milestone: milestone
-  = paginate @dashboard_milestones, theme: "gitlab"
+  = paginate @milestones, theme: "gitlab"
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 2fe14c6388c3255b6e014400d45ddbb1f75d65ad..3536bbeaf4b9452d98a05f995234fed512b04021 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -1,18 +1,22 @@
-- page_title @dashboard_milestone.title, "Milestones"
-%h4.page-title
-  .issue-box{ class: "issue-box-#{@dashboard_milestone.closed? ? 'closed' : 'open'}" }
-    - if @dashboard_milestone.closed?
-      Closed
-    - else
-      Open
-  Milestone #{@dashboard_milestone.title}
+- page_title @milestone.title, "Milestones"
+- header_title "Milestones", dashboard_milestones_path
 
-%hr
-- if (@dashboard_milestone.total_items_count == @dashboard_milestone.closed_items_count) && @dashboard_milestone.active?
-  .alert.alert-success
-    %span All issues for this milestone are closed. You may close the milestone now.
+.issuable-details
+  .page-title
+    .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+      - if @milestone.closed?
+        Closed
+      - else
+        Open
+    Milestone #{@milestone.title}
+
+  .gray-content-block.middle-block
+    %h2.issue-title
+      = gfm escape_once(@milestone.title)
 
-.description
+- if @milestone.complete? && @milestone.active?
+  .alert.alert-success.prepend-top-default
+    %span All issues for this milestone are closed. You may close the milestone now.
 
 .table-holder
   %table.table
@@ -22,7 +26,7 @@
         %th Open issues
         %th State
         %th Due date
-    - @dashboard_milestone.milestones.each do |milestone|
+    - @milestone.milestones.each do |milestone|
       %tr
         %td
           = link_to "#{milestone.project.name_with_namespace}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
@@ -39,46 +43,60 @@
 .context
   %p.lead
     Progress:
-    #{@dashboard_milestone.closed_items_count} closed
+    #{@milestone.closed_items_count} closed
     &ndash;
-    #{@dashboard_milestone.open_items_count} open
-  = milestone_progress_bar(@dashboard_milestone)
+    #{@milestone.open_items_count} open
+  = milestone_progress_bar(@milestone)
 
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
-      %span.badge= @dashboard_milestone.issue_count
+      %span.badge= @milestone.issue_count
   %li
     = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
       Merge Requests
-      %span.badge= @dashboard_milestone.merge_requests_count
+      %span.badge= @milestone.merge_requests_count
   %li
     = link_to '#tab-participants', 'data-toggle' => 'tab' do
       Participants
-      %span.badge= @dashboard_milestone.participants.count
-
-  .pull-right
-    = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @dashboard_milestone.title), class: "btn  edit-milestone-link btn-grouped"
+      %span.badge= @milestone.participants.count
 
 .tab-content
   .tab-pane.active#tab-issues
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        = link_to 'Browse Issues', issues_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All issues in this milestone
+
+    .row.prepend-top-default
       .col-md-6
-        = render 'issues', title: "Open", issues: @dashboard_milestone.opened_issues
+        = render 'issues', title: "Open", issues: @milestone.opened_issues
       .col-md-6
-        = render 'issues', title: "Closed", issues: @dashboard_milestone.closed_issues
+        = render 'issues', title: "Closed", issues: @milestone.closed_issues
 
   .tab-pane#tab-merge-requests
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        = link_to 'Browse Merge Requests', merge_requests_dashboard_path(milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All merge requests in this milestone
+
+    .row.prepend-top-default
       .col-md-6
-        = render 'merge_requests', title: "Open", merge_requests: @dashboard_milestone.opened_merge_requests
+        = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
       .col-md-6
-        = render 'merge_requests', title: "Closed", merge_requests: @dashboard_milestone.closed_merge_requests
+        = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
 
   .tab-pane#tab-participants
+    .gray-content-block.middle-block
+      .oneline
+        All participants to this milestone
     %ul.bordered-list
-      - @dashboard_milestone.participants.each do |user|
+      - @milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
             = image_tag avatar_icon(user, 32), class: "avatar s32"
diff --git a/app/views/dashboard/projects/index.atom.builder b/app/views/dashboard/projects/index.atom.builder
index d2c5148684131f211bbf7cb5126f8942d7ad8929..c8c219f4cca00870d86877536e60a764431d281c 100644
--- a/app/views/dashboard/projects/index.atom.builder
+++ b/app/views/dashboard/projects/index.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: dashboard_projects_url(format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: dashboard_projects_url, rel: "alternate", type: "text/html"
   xml.id      dashboard_projects_url
-  xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+  xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
     event_to_atom(xml, event)
diff --git a/app/views/dashboard/projects/index.html.haml b/app/views/dashboard/projects/index.html.haml
index 7a16b811f6bcf4fea0e981d6719b59a732b0d0c4..53abf274bdbf68ca7389de872b2046c0f153edfb 100644
--- a/app/views/dashboard/projects/index.html.haml
+++ b/app/views/dashboard/projects/index.html.haml
@@ -3,7 +3,7 @@
     = auto_discovery_link_tag(:atom, dashboard_projects_url(format: :atom, private_token: current_user.private_token), title: "All activity")
 
 - page_title    "Projects"
-- header_title  "Projects", root_path
+- header_title  "Projects", dashboard_projects_path
 
 = render 'dashboard/projects_head'
 
diff --git a/app/views/dashboard/projects/starred.html.haml b/app/views/dashboard/projects/starred.html.haml
index f75f2e0a32a61d232ae7f6142f740089582ec077..70705923d42b9cf64d3536abce06cff1c7372a31 100644
--- a/app/views/dashboard/projects/starred.html.haml
+++ b/app/views/dashboard/projects/starred.html.haml
@@ -1,5 +1,5 @@
 - page_title "Starred Projects"
-- header_title "Projects", projects_path
+- header_title "Projects", dashboard_projects_path
 
 = render 'dashboard/projects_head'
 
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 9a1331b2549361fbb5b1ed389b6e702bdc4a761e..41ad2c231d4d1cfaa0c2d3478d2e185ca579dcbb 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -7,34 +7,26 @@
       %h3 Sign in
   .login-body
     - if form_based_providers.any?
-      - if form_based_providers.count >= 2 || signin_enabled?
-        %ul.nav.nav-tabs
-          - if crowd_enabled?
-            %li.active
-              = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
-          - @ldap_servers.each_with_index do |server, i|
-            %li{class: (:active if i.zero? && !crowd_enabled?)}
-              = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
-          - if signin_enabled?
-            %li
-              = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
-        .tab-content
-          - if crowd_enabled?
-            %div.tab-pane.active{id: "tab-crowd"}
-              = render 'devise/sessions/new_crowd'
-          - @ldap_servers.each_with_index do |server, i|
-            %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
-              = render 'devise/sessions/new_ldap', server: server
-          - if signin_enabled?
-            %div#tab-signin.tab-pane
-              = render 'devise/sessions/new_base'
-      - else
+      %ul.nav.nav-tabs
         - if crowd_enabled?
-          = render 'devise/sessions/new_crowd'
-        - elsif @ldap_servers.any?
-          = render 'devise/sessions/new_ldap', server: @ldap_servers.first
-        - elsif signin_enabled?
-          = render 'devise/sessions/new_base'
+          %li.active
+            = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
+        - @ldap_servers.each_with_index do |server, i|
+          %li{class: (:active if i.zero? && !crowd_enabled?)}
+            = link_to server['label'], "#tab-#{server['provider_name']}", 'data-toggle' => 'tab'
+        - if signin_enabled?
+          %li
+            = link_to 'Standard', '#tab-signin', 'data-toggle' => 'tab'
+      .tab-content
+        - if crowd_enabled?
+          %div.tab-pane.active{id: "tab-crowd"}
+            = render 'devise/sessions/new_crowd'
+        - @ldap_servers.each_with_index do |server, i|
+          %div.tab-pane{id: "tab-#{server['provider_name']}", class: (:active if i.zero? && !crowd_enabled?)}
+            = render 'devise/sessions/new_ldap', server: server
+        - if signin_enabled?
+          %div#tab-signin.tab-pane
+            = render 'devise/sessions/new_base'
 
     - elsif signin_enabled?
       = render 'devise/sessions/new_base'
diff --git a/app/views/explore/projects/_filter.html.haml b/app/views/explore/projects/_filter.html.haml
index 2761272aa8ab15d40a2d575da4f2da92bbf391e6..28b12c8dca876a104d88a2a5b3578a2d49648880 100644
--- a/app/views/explore/projects/_filter.html.haml
+++ b/app/views/explore/projects/_filter.html.haml
@@ -3,7 +3,7 @@
     .form-group
       = 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"
+      = button_tag 'Search', class: "btn"
 
 .pull-right.hidden-sm.hidden-xs
   - if current_user
diff --git a/app/views/explore/projects/index.html.haml b/app/views/explore/projects/index.html.haml
index 67e38ca3127f1adf767ecc06cd3a446b84058c27..76bdd68fd764b3f8a7f9f8d0f7683adb0fc06e35 100644
--- a/app/views/explore/projects/index.html.haml
+++ b/app/views/explore/projects/index.html.haml
@@ -1,5 +1,5 @@
 - page_title    "Projects"
-- header_title  "Projects", root_path
+- header_title  "Projects", dashboard_projects_path
 
 - if current_user
   = render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/starred.html.haml b/app/views/explore/projects/starred.html.haml
index 596cb0a96cd8f72d7bb192cb26e106135202f55a..e30c3633223c026e86400fa0c47a7283e63d0aa9 100644
--- a/app/views/explore/projects/starred.html.haml
+++ b/app/views/explore/projects/starred.html.haml
@@ -1,5 +1,5 @@
 - page_title    "Projects"
-- header_title  "Projects", root_path
+- header_title  "Projects", dashboard_projects_path
 
 - if current_user
   = render 'dashboard/projects_head'
diff --git a/app/views/explore/projects/trending.html.haml b/app/views/explore/projects/trending.html.haml
index 5ea6d81c5b9b9c1b054cc2a0f0298339edacc87a..1412b19acde9b2fa25ea5f5596a8e965d4d106ec 100644
--- a/app/views/explore/projects/trending.html.haml
+++ b/app/views/explore/projects/trending.html.haml
@@ -1,5 +1,5 @@
 - page_title    "Projects"
-- header_title  "Projects", root_path
+- header_title  "Projects", dashboard_projects_path
 
 - if current_user
   = render 'dashboard/projects_head'
diff --git a/app/views/groups/group_members/_group_member.html.haml b/app/views/groups/group_members/_group_member.html.haml
index 3c19381321aec37e36924f7c34635726ea265fde..a79a0fcdc8e3a5a1a5d8b015078bf9a3cf997583 100644
--- a/app/views/groups/group_members/_group_member.html.haml
+++ b/app/views/groups/group_members/_group_member.html.haml
@@ -1,11 +1,10 @@
 - user = member.user
 - return unless user || member.invite?
-- show_roles = true if show_roles.nil?
 
 %li{class: "#{dom_class(member)} js-toggle-container", id: dom_id(member)}
   %span{class: ("list-item-name" if show_controls)}
     - if member.user
-      = image_tag avatar_icon(user, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
       %strong
         = link_to user.name, user_path(user)
       %span.cgray= user.username
@@ -15,7 +14,7 @@
         %label.label.label-danger
           %strong Blocked
     - else
-      = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
       %strong
         = member.invite_email
       %span.cgray
@@ -25,18 +24,19 @@
           = link_to member.created_by.name, user_path(member.created_by)
         = time_ago_with_tooltip(member.created_at)
 
-      - if show_controls && can?(current_user, :admin_group_member, member)
+      - if show_controls && can?(current_user, :admin_group_member, @group)
         = link_to resend_invite_group_group_member_path(@group, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
           Resend invite
 
-  - if show_roles
+  - if should_user_see_group_roles?(current_user, @group)
     %span.pull-right
-      %strong= member.human_access
+      %strong.member-access-level= member.human_access
       - if show_controls
         - if can?(current_user, :update_group_member, member)
           = button_tag class: "btn-xs btn js-toggle-button",
                        title: 'Edit access level', type: 'button' do
             %i.fa.fa-pencil-square-o
+
         - if can?(current_user, :destroy_group_member, member)
           &nbsp;
           - if current_user == user
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 15d289471c9a743ac532d4b6b86588d98577971c..335bf036074367eb98b460408a7e231a2fb1fa2f 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -1,40 +1,35 @@
 - page_title "Members"
 - header_title group_title(@group, "Members", group_group_members_path(@group))
-- show_roles = should_user_see_group_roles?(current_user, @group)
-
-- if show_roles
-  %p.light
-    Members of group have access to all group projects.
-    Read more about permissions
-    %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
-
-
-.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', spellcheck: false }
-    = button_tag 'Search', class: 'btn'
+- @blank_container = true
 
+.group-members-page
   - if current_user && current_user.can?(:admin_group_member, @group)
-    .pull-right
-      = button_tag class: 'btn btn-new js-toggle-button', type: 'button' do
-        Add members
-        %i.fa.fa-chevron-down
-
-    .js-toggle-content.hide.new-group-member-holder
-      = render "new_group_member"
-
-.panel.panel-default.prepend-top-20
-  .panel-heading
-    %strong #{@group.name}
-    group members
-    %small
-      (#{@members.total_count})
-  %ul.well-list
-    - @members.each do |member|
-      = render 'groups/group_members/group_member', member: member, show_roles: show_roles, show_controls: true
-
-= paginate @members, theme: 'gitlab'
+    .panel.panel-default
+      .panel-heading
+        Add new user to group
+      .panel-body
+        - if should_user_see_group_roles?(current_user, @group)
+          %p.light
+            Members of group have access to all group projects.
+        .new-group-member-holder
+          = render "new_group_member"
+
+  .panel.panel-default
+    .panel-heading
+      %strong #{@group.name}
+      group members
+      %small
+        (#{@members.total_count})
+      .pull-right
+        = 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', spellcheck: false }
+          = button_tag class: 'btn', title: 'Search' do
+            = icon("search")
+    %ul.content-list
+      - @members.each do |member|
+        = render 'groups/group_members/group_member', member: member, show_controls: true
+    = paginate @members, theme: 'gitlab'
 
 :javascript
   $('form.member-search-form').on('submit', function(event) {
diff --git a/app/views/groups/group_members/update.js.haml b/app/views/groups/group_members/update.js.haml
index 5bad48abafd0e908d7dcf6eca0de33c39fefdfc2..df726e2b2b9f355528af9b3ba3247c159815847c 100644
--- a/app/views/groups/group_members/update.js.haml
+++ b/app/views/groups/group_members/update.js.haml
@@ -1,2 +1,2 @@
 :plain
-  $("##{dom_id(@member)}").replaceWith('#{escape_javascript(render(@member, member: @member, show_controls: true))}');
+  $("##{dom_id(@group_member)}").replaceWith('#{escape_javascript(render(@group_member, member: @group_member, show_controls: true))}');
diff --git a/app/views/groups/milestones/_milestone.html.haml b/app/views/groups/milestones/_milestone.html.haml
index 41dffdd2fb83fdb3d01e337ebf48c17de5bf6b2a..a20bf75bc39e2d320651479f0a116a9f90935a9c 100644
--- a/app/views/groups/milestones/_milestone.html.haml
+++ b/app/views/groups/milestones/_milestone.html.haml
@@ -22,7 +22,7 @@
             %span.label.label-gray
               = milestone.project.name
     .col-sm-6
-      - if can?(current_user, :admin_group, @group)
+      - if can?(current_user, :admin_milestones, @group)
         - if milestone.closed?
           = link_to 'Reopen Milestone', group_milestone_path(@group, milestone.safe_title, title: milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-xs btn-grouped btn-reopen"
         - else
diff --git a/app/views/groups/milestones/index.html.haml b/app/views/groups/milestones/index.html.haml
index 2bbcad5fdfb7f56bf6ca601f96ca438ae438d5d1..84ec77c61888e082a057a9e6e74230bf8520e416 100644
--- a/app/views/groups/milestones/index.html.haml
+++ b/app/views/groups/milestones/index.html.haml
@@ -3,15 +3,22 @@
 
 = render 'shared/milestones_filter'
 .gray-content-block
-  Only milestones from
-  %strong #{@group.name}
-  group are listed here.
+  - if can?(current_user, :admin_milestones, @group)
+    .pull-right
+      %span.pull-right.hidden-xs
+        = link_to new_group_milestone_path(@group), class: "btn btn-new" do
+          New Milestone
+
+  .oneline
+    Only milestones from
+    %strong #{@group.name}
+    group are listed here.
 .milestones
   %ul.content-list
-    - if @group_milestones.blank?
+    - if @milestones.blank?
       %li
         .nothing-here-block No milestones to show
     - else
-      - @group_milestones.each do |milestone|
+      - @milestones.each do |milestone|
         = render 'milestone', milestone: milestone
-  = paginate @group_milestones, theme: "gitlab"
+  = paginate @milestones, theme: "gitlab"
diff --git a/app/views/groups/milestones/new.html.haml b/app/views/groups/milestones/new.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..800bac4ef023478e9d4e84e7a5e1c077738c51f2
--- /dev/null
+++ b/app/views/groups/milestones/new.html.haml
@@ -0,0 +1,48 @@
+- page_title "Milestones"
+- header_title group_title(@group, "Milestones", group_milestones_path(@group))
+
+%h3.page-title
+  New Milestone
+
+%p.light
+  This will create milestone in every selected project
+%hr
+
+= form_for @milestone, url: group_milestones_path(@group), html: { class: 'form-horizontal milestone-form gfm-form js-requires-input' }  do |f|
+  .row
+    .col-md-6
+      .form-group
+        = f.label :title, "Title", class: "control-label"
+        .col-sm-10
+          = f.text_field :title, maxlength: 255, class: "form-control js-quick-submit", required: true
+          %p.hint Required
+      .form-group.milestone-description
+        = f.label :description, "Description", class: "control-label"
+        .col-sm-10
+          = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
+            = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
+            .clearfix
+            .error-alert
+      .form-group
+        = f.label :projects, "Projects", class: "control-label"
+        .col-sm-10
+          = f.collection_select :project_ids, @group.projects, :id, :name,
+            { selected: @group.projects.map(&:id) }, multiple: true, class: 'select2'
+
+    .col-md-6
+      .form-group
+        = f.label :due_date, "Due Date", class: "control-label"
+        .col-sm-10= f.hidden_field :due_date
+        .col-sm-10
+          .datepicker
+
+  .form-actions
+    = f.submit 'Create Milestone', class: "btn-create btn"
+    = link_to "Cancel", group_milestones_path(@group), class: "btn btn-cancel"
+
+
+:javascript
+  $(".datepicker").datepicker({
+    dateFormat: "yy-mm-dd",
+    onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
+  }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index a92ad5d751b985815ddcf3dadae39dacb1caea5c..3c1d8815013a5883b7fef63940dcb729628502c2 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -1,26 +1,28 @@
-- page_title @group_milestone.title, "Milestones"
+- page_title @milestone.title, "Milestones"
 = render "header_title"
 
-%h4.page-title
-  .issue-box{ class: "issue-box-#{@group_milestone.closed? ? 'closed' : 'open'}" }
-    - if @group_milestone.closed?
-      Closed
-    - else
-      Open
-  Milestone #{@group_milestone.title}
-  .pull-right
-    - if can?(current_user, :admin_group, @group)
-      - if @group_milestone.active?
-        = link_to 'Close Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-sm btn-close"
+.issuable-details
+  .page-title
+    .issue-box{ class: "issue-box-#{@milestone.closed? ? 'closed' : 'open'}" }
+      - if @milestone.closed?
+        Closed
       - else
-        = link_to 'Reopen Milestone', group_milestone_path(@group, @group_milestone.safe_title, title: @group_milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-sm btn-grouped btn-reopen"
+        Open
+    Milestone #{@milestone.title}
+    .pull-right
+      - if can?(current_user, :admin_milestones, @group)
+        - if @milestone.active?
+          = link_to 'Close Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :close }), method: :put, class: "btn btn-grouped btn-close"
+        - else
+          = link_to 'Reopen Milestone', group_milestone_path(@group, @milestone.safe_title, title: @milestone.title, milestone: {state_event: :activate }), method: :put, class: "btn btn-grouped btn-reopen"
 
-%hr
-- if (@group_milestone.total_items_count == @group_milestone.closed_items_count) && @group_milestone.active?
-  .alert.alert-success
-    %span All issues for this milestone are closed. You may close the milestone now.
+  .gray-content-block.middle-block
+    %h2.issue-title
+      = gfm escape_once(@milestone.title)
 
-.description
+- if @milestone.complete? && @milestone.active?
+  .alert.alert-success.prepend-top-default
+    %span All issues for this milestone are closed. You may close the milestone now.
 
 .table-holder
   %table.table
@@ -30,7 +32,7 @@
         %th Open issues
         %th State
         %th Due date
-    - @group_milestone.milestones.each do |milestone|
+    - @milestone.milestones.each do |milestone|
       %tr
         %td
           = link_to "#{milestone.project.name}", namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone)
@@ -47,46 +49,61 @@
 .context
   %p.lead
     Progress:
-    #{@group_milestone.closed_items_count} closed
+    #{@milestone.closed_items_count} closed
     &ndash;
-    #{@group_milestone.open_items_count} open
-  = milestone_progress_bar(@group_milestone)
+    #{@milestone.open_items_count} open
+  = milestone_progress_bar(@milestone)
 
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
-      %span.badge= @group_milestone.issue_count
+      %span.badge= @milestone.issue_count
   %li
     = link_to '#tab-merge-requests', 'data-toggle' => 'tab' do
       Merge Requests
-      %span.badge= @group_milestone.merge_requests_count
+      %span.badge= @milestone.merge_requests_count
   %li
     = link_to '#tab-participants', 'data-toggle' => 'tab' do
       Participants
-      %span.badge= @group_milestone.participants.count
-
-  .pull-right
-    = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @group_milestone.title), class: "btn  edit-milestone-link btn-grouped"
+      %span.badge= @milestone.participants.count
 
 .tab-content
   .tab-pane.active#tab-issues
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        = link_to 'Browse Issues', issues_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All issues in this milestone
+
+    .row.prepend-top-default
       .col-md-6
-        = render 'issues', title: "Open", issues: @group_milestone.opened_issues
+        = render 'issues', title: "Open", issues: @milestone.opened_issues
       .col-md-6
-        = render 'issues', title: "Closed", issues: @group_milestone.closed_issues
+        = render 'issues', title: "Closed", issues: @milestone.closed_issues
 
   .tab-pane#tab-merge-requests
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        = link_to 'Browse Merge Requests', merge_requests_group_path(@group, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All merge requests in this milestone
+
+    .row.prepend-top-default
       .col-md-6
-        = render 'merge_requests', title: "Open", merge_requests: @group_milestone.opened_merge_requests
+        = render 'merge_requests', title: "Open", merge_requests: @milestone.opened_merge_requests
       .col-md-6
-        = render 'merge_requests', title: "Closed", merge_requests: @group_milestone.closed_merge_requests
+        = render 'merge_requests', title: "Closed", merge_requests: @milestone.closed_merge_requests
 
   .tab-pane#tab-participants
+    .gray-content-block.middle-block
+      .oneline
+        All participants to this milestone
+
     %ul.bordered-list
-      - @group_milestone.participants.each do |user|
+      - @milestone.participants.each do |user|
         %li
           = link_to user, title: user.name, class: "darken" do
             = image_tag avatar_icon(user, 32), class: "avatar s32"
diff --git a/app/views/groups/show.atom.builder b/app/views/groups/show.atom.builder
index a91d1a6e94b23c44b5d2fdae571ecdcb2752806b..7ea574434c3b81d3072fda3f794c0db14b7f1da2 100644
--- a/app/views/groups/show.atom.builder
+++ b/app/views/groups/show.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: group_url(@group, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: group_url(@group), rel: "alternate", type: "text/html"
   xml.id      group_url(@group)
-  xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+  xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
     event_to_atom(xml, event)
diff --git a/app/views/import/bitbucket/status.html.haml b/app/views/import/bitbucket/status.html.haml
index 1f09a27e2d6d280b7bf7cc9d5eb1bb76b0f8bb59..aec2e836c9fdbac543af0252b8628d77f6eadd6c 100644
--- a/app/views/import/bitbucket/status.html.haml
+++ b/app/views/import/bitbucket/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "Bitbucket import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-bitbucket
   Import projects from Bitbucket
diff --git a/app/views/import/fogbugz/new.html.haml b/app/views/import/fogbugz/new.html.haml
index e1bb88ca4ed4bb6f80ce233b1bf8a0d19c85dcfb..5515fad6f48c98a2171496b8fd06f484c9d867b4 100644
--- a/app/views/import/fogbugz/new.html.haml
+++ b/app/views/import/fogbugz/new.html.haml
@@ -1,4 +1,5 @@
 - page_title "FogBugz Import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-bug
   Import projects from FogBugz
diff --git a/app/views/import/fogbugz/new_user_map.html.haml b/app/views/import/fogbugz/new_user_map.html.haml
index bc3c90294e3bcb0afc4e6180bad67a20210843ee..07338736bacd48449a7ea68a8321a9429fff869c 100644
--- a/app/views/import/fogbugz/new_user_map.html.haml
+++ b/app/views/import/fogbugz/new_user_map.html.haml
@@ -1,4 +1,5 @@
 - page_title 'User map', 'FogBugz import'
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-bug
   Import projects from FogBugz
diff --git a/app/views/import/fogbugz/status.html.haml b/app/views/import/fogbugz/status.html.haml
index b902006597bd64239ba9fffa917dd044bbce21e8..6ee16c8be4b95386deb10702e3650de7a341312b 100644
--- a/app/views/import/fogbugz/status.html.haml
+++ b/app/views/import/fogbugz/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "FogBugz import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-bug
   Import projects from FogBugz
diff --git a/app/views/import/github/status.html.haml b/app/views/import/github/status.html.haml
index 0699321c8c00ac1cf624af837b78d9631cb96ba8..1416ee5bd5a0d7e00bbd6e9ba6ffd111f77c99b9 100644
--- a/app/views/import/github/status.html.haml
+++ b/app/views/import/github/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "GitHub import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-github
   Import projects from GitHub
diff --git a/app/views/import/gitlab/status.html.haml b/app/views/import/gitlab/status.html.haml
index f4a2b33af21fa9d851f392c9bac0ba364072a85f..911a55eb85dfc20eeb98ff2477796f610af5e051 100644
--- a/app/views/import/gitlab/status.html.haml
+++ b/app/views/import/gitlab/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "GitLab.com import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-heart
   Import projects from GitLab.com
diff --git a/app/views/import/gitorious/status.html.haml b/app/views/import/gitorious/status.html.haml
index 71752d21efab67c3722cd75274826d51574d8526..6b0fa1edf8ce2d5a6e20b859b6d594de0208005c 100644
--- a/app/views/import/gitorious/status.html.haml
+++ b/app/views/import/gitorious/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "Gitorious import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.icon-gitorious.icon-gitorious-big
   Import projects from Gitorious.org
diff --git a/app/views/import/google_code/new.html.haml b/app/views/import/google_code/new.html.haml
index 9c64e0a009fb73931f3845306911929d33b5ef6e..5d2f149cd5f013810b83045674f5cffc4caab95b 100644
--- a/app/views/import/google_code/new.html.haml
+++ b/app/views/import/google_code/new.html.haml
@@ -1,4 +1,5 @@
 - page_title "Google Code import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
@@ -6,7 +7,7 @@
 
 = form_tag callback_import_google_code_path, class: 'form-horizontal', multipart: true do
   %p
-    Follow the steps below to export your Google Code project data. 
+    Follow the steps below to export your Google Code project data.
     In the next step, you'll be able to select the projects you want to import.
   %ol
     %li
diff --git a/app/views/import/google_code/new_user_map.html.haml b/app/views/import/google_code/new_user_map.html.haml
index e53ebda7dc19683ab7d601aa73a4735d85504a20..0738b3db1ebcc5d1ab3cc0da8080a3b6814d1488 100644
--- a/app/views/import/google_code/new_user_map.html.haml
+++ b/app/views/import/google_code/new_user_map.html.haml
@@ -1,4 +1,5 @@
 - page_title "User map", "Google Code import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
@@ -8,31 +9,31 @@
   %p
     Customize how Google Code email addresses and usernames are imported into GitLab.
     In the next step, you'll be able to select the projects you want to import.
-  %p 
+  %p
     The user map is a JSON document mapping the Google Code users that participated on your projects to the way their email addresses and usernames will be imported into GitLab. You can change this by changing the value on the right hand side of <code>:</code>. Be sure to preserve the surrounding double quotes, other punctuation and the email address or username on the left hand side.
   %ul
     %li
       %strong Default: Directly import the Google Code email address or username
       %p
-        <code>"johnsmith@example.com": "johnsm...@example.com"</code> 
-        will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com. 
+        <code>"johnsmith@example.com": "johnsm...@example.com"</code>
+        will add "By johnsm...@example.com" to all issues and comments originally created by johnsmith@example.com.
         The email address or username is masked to ensure the user's privacy.
     %li
       %strong Map a Google Code user to a GitLab user
       %p
-        <code>"johnsmith@example.com": "@johnsmith"</code> 
-        will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com, 
+        <code>"johnsmith@example.com": "@johnsmith"</code>
+        will add "By <a href="#">@johnsmith</a>" to all issues and comments originally created by johnsmith@example.com,
         and will set <a href="#">@johnsmith</a> as the assignee on all issues originally assigned to johnsmith@example.com.
     %li
       %strong Map a Google Code user to a full name
       %p
-        <code>"johnsmith@example.com": "John Smith"</code> 
+        <code>"johnsmith@example.com": "John Smith"</code>
         will add "By John Smith" to all issues and comments originally created by johnsmith@example.com.
     %li
       %strong Map a Google Code user to a full email address
       %p
-        <code>"johnsmith@example.com": "johnsmith@example.com"</code> 
-        will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com. 
+        <code>"johnsmith@example.com": "johnsmith@example.com"</code>
+        will add "By <a href="#">johnsmith@example.com</a>" to all issues and comments originally created by johnsmith@example.com.
         By default, the email address or username is masked to ensure the user's privacy. Use this option if you want to show the full email address.
 
   .form-group
diff --git a/app/views/import/google_code/status.html.haml b/app/views/import/google_code/status.html.haml
index 8c64fd27e604373ce0d2d5b12dd7f20380c8e7a4..175ef6921cd19a656c652467ebb9c889127c1421 100644
--- a/app/views/import/google_code/status.html.haml
+++ b/app/views/import/google_code/status.html.haml
@@ -1,4 +1,5 @@
 - page_title "Google Code import"
+- header_title "Projects", root_path
 %h3.page-title
   %i.fa.fa-google
   Import projects from Google Code
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index c08a7b8074474222551ef746e14cda1269c08bf8..3ca30d3baabd89114d18b52495d25f56678c08f5 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -13,6 +13,10 @@
           %li.visible-sm.visible-xs
             = link_to search_path, title: 'Search', data: {toggle: 'tooltip', placement: 'bottom'} do
               = icon('search')
+          - if session[:impersonator_id]
+            %li.impersonation
+              = link_to stop_impersonation_admin_users_path, method: :delete, title: 'Stop impersonation', data: { toggle: 'tooltip', placement: 'bottom' } do
+                = icon('user-secret fw')
           - if current_user.is_admin?
             %li
               = link_to admin_root_path, title: 'Admin area', data: {toggle: 'tooltip', placement: 'bottom'} do
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index a59939ccd31f5335652e8c161fdbca11057598af..377a99e719aa4bfb18784b2699f54c63a0151c39 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -34,7 +34,7 @@
         %span
           Protected Branches
 
-    - if @project.gitlab_ci?
+    - if @project.builds_enabled?
       = nav_link(controller: :runners) do
         = link_to namespace_project_runners_path(@project.namespace, @project), title: 'Runners', data: {placement: 'right'} do
           = icon('cog fw')
diff --git a/app/views/notify/project_was_moved_email.text.erb b/app/views/notify/project_was_moved_email.text.erb
index d8a23dabf499789a10a626814d047c9a7cf9e68c..b2c5f71e46505e16aaa8c2c0342e46bdd5138768 100644
--- a/app/views/notify/project_was_moved_email.text.erb
+++ b/app/views/notify/project_was_moved_email.text.erb
@@ -1,4 +1,4 @@
-Project #{@old_path_with_namespace} was moved to another location
+Project <%= @old_path_with_namespace %> was moved to another location
 
 The project is now located under 
 <%= namespace_project_url(@project.namespace, @project) %>
diff --git a/app/views/profiles/applications.html.haml b/app/views/profiles/applications.html.haml
index 2342936a5d50db91cfc70e749a4faf6f635f0219..0436c2213da003078022bc0462f2cdabf033f6ed 100644
--- a/app/views/profiles/applications.html.haml
+++ b/app/views/profiles/applications.html.haml
@@ -15,24 +15,25 @@
       .pull-right
         = link_to 'New Application', new_oauth_application_path, class: 'btn btn-success'
     - if @applications.any?
-      %table.table.table-striped
-        %thead
-          %tr
-            %th Name
-            %th Callback URL
-            %th Clients
-            %th
-            %th
-        %tbody
-          - @applications.each do |application|
-            %tr{:id => "application_#{application.id}"}
-              %td= link_to application.name, oauth_application_path(application)
-              %td
-                - application.redirect_uri.split.each do |uri|
-                  %div= uri
-              %td= application.access_tokens.count
-              %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm'
-              %td= render 'doorkeeper/applications/delete_form', application: application
+      .table-holder
+        %table.table.table-striped
+          %thead
+            %tr
+              %th Name
+              %th Callback URL
+              %th Clients
+              %th
+              %th
+          %tbody
+            - @applications.each do |application|
+              %tr{:id => "application_#{application.id}"}
+                %td= link_to application.name, oauth_application_path(application)
+                %td
+                  - application.redirect_uri.split.each do |uri|
+                    %div= uri
+                %td= application.access_tokens.count
+                %td= link_to 'Edit', edit_oauth_application_path(application), class: 'btn btn-link btn-sm'
+                %td= render 'doorkeeper/applications/delete_form', application: application
 
 .oauth-authorized-applications.prepend-top-20
   - if user_oauth_applications?
@@ -40,29 +41,30 @@
       Authorized applications
 
   - if @authorized_tokens.any?
-    %table.table.table-striped
-      %thead
-        %tr
-          %th Name
-          %th Authorized At
-          %th Scope
-          %th
-      %tbody
-        - @authorized_apps.each do |app|
-          - token = app.authorized_tokens.order('created_at desc').first
-          %tr{:id => "application_#{app.id}"}
-            %td= app.name
-            %td= token.created_at
-            %td= token.scopes
-            %td= render 'doorkeeper/authorized_applications/delete_form', application: app
-        - @authorized_anonymous_tokens.each do |token|
+    .table-holder
+      %table.table.table-striped
+        %thead
           %tr
-            %td
-              Anonymous
-              %div.help-block
-                %em Authorization was granted by entering your username and password in the application.
-            %td= token.created_at
-            %td= token.scopes
-            %td= render 'doorkeeper/authorized_applications/delete_form', token: token
+            %th Name
+            %th Authorized At
+            %th Scope
+            %th
+        %tbody
+          - @authorized_apps.each do |app|
+            - token = app.authorized_tokens.order('created_at desc').first
+            %tr{:id => "application_#{app.id}"}
+              %td= app.name
+              %td= token.created_at
+              %td= token.scopes
+              %td= render 'doorkeeper/authorized_applications/delete_form', application: app
+          - @authorized_anonymous_tokens.each do |token|
+            %tr
+              %td
+                Anonymous
+                %div.help-block
+                  %em Authorization was granted by entering your username and password in the application.
+              %td= token.created_at
+              %td= token.scopes
+              %td= render 'doorkeeper/authorized_applications/delete_form', token: token
   - else
     %p.light You don't have any authorized applications
diff --git a/app/views/profiles/keys/_key_table.html.haml b/app/views/profiles/keys/_key_table.html.haml
index ef0075aad3b20598c45a21fed3453a8cd65131b6..8c9d546af4c395a459849a8c32628e070c3fc434 100644
--- a/app/views/profiles/keys/_key_table.html.haml
+++ b/app/views/profiles/keys/_key_table.html.haml
@@ -1,6 +1,6 @@
 - is_admin = defined?(admin) ? true : false
-.panel.panel-default
-  - if @keys.any?
+- if @keys.any?
+  .table-holder
     %table.table
       %thead.panel-heading
         %tr
@@ -11,9 +11,9 @@
       %tbody
         - @keys.each do |key|
           = render 'profiles/keys/key', key: key, is_admin: is_admin
-  - else
-    .nothing-here-block
-      - if is_admin
-        User has no ssh keys
-      - else
-        There are no SSH keys with access to your account.
+- else
+  .nothing-here-block
+    - if is_admin
+      User has no ssh keys
+    - else
+      There are no SSH keys with access to your account.
diff --git a/app/views/profiles/keys/index.html.haml b/app/views/profiles/keys/index.html.haml
index 14adba1c797e61b53f298e769515a11d4738da4b..17a4195030e767aa9b4384c9d8a38b315d41129d 100644
--- a/app/views/profiles/keys/index.html.haml
+++ b/app/views/profiles/keys/index.html.haml
@@ -3,7 +3,9 @@
 
 .gray-content-block.top-block
   .pull-right
-    = link_to "Add SSH Key", new_profile_key_path, class: "btn btn-new"
+    = link_to new_profile_key_path, class: "btn btn-new" do
+      = icon('plus')
+      Add SSH Key
   .oneline
     Before you can add an SSH key you need to
     = link_to "generate it.", help_page_path("ssh", "README")
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 8eebd96b674f57a9a6a19ffaeb68626826339200..0bcadc965fa00568bbb05ec4bfd3771d11e86ac1 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -50,12 +50,10 @@
             Watch
           %p You will receive notifications for any activity
 
-  .form-actions
+  .gray-content-block
     = f.submit 'Save changes', class: "btn btn-create"
 
-.clearfix
-  %hr
-.row.all-notifications
+.row.all-notifications.prepend-top-default
   .col-md-6
     %p
       You can also specify notification level per group or per project.
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 8c0980369fd867d700b331efa7fc3ed5cc9a3340..c1669ac046b4207ae92914d078b0871e8e8bec0b 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -1,5 +1,5 @@
 - empty_repo = @project.empty_repo?
-.project-home-panel.clearfix{:class => ("empty-project" if empty_repo)}
+.project-home-panel.cover-block.clearfix{:class => ("empty-project" if empty_repo)}
   .project-identicon-holder
     = project_icon(@project, alt: '', class: 'project-avatar avatar s90')
   .project-home-desc
@@ -12,23 +12,29 @@
         Forked from
         = link_to project_path(forked_from_project) do
           = forked_from_project.namespace.try(:name)
+  .cover-controls.left
+    .visibility-level-label.has_tooltip{title: project_visibility_level_description(@project.visibility_level), data: { container: 'body' } }
+      = visibility_level_icon(@project.visibility_level, fw: false)
+      = visibility_level_label(@project.visibility_level)
 
-
+  .cover-controls
+    - if can?(current_user, :admin_project, @project)
+      = link_to edit_project_path(@project), class: 'btn btn-gray' do
+        = icon('pencil')
+    - if current_user
+      &nbsp;
+      = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), class: 'btn btn-gray' do
+        = icon('rss')
 
   .project-repo-buttons
     .split-one
       = render 'projects/buttons/star'
+      = render 'projects/buttons/fork'
 
-      - unless empty_repo
-        = render 'projects/buttons/fork'
-    
     = render "shared/clone_panel"
-    .split-repo-buttons 
-      - unless empty_repo
-        - if can? current_user, :download_code, @project
-          = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn', rel: 'nofollow' do
-            = icon('download fw')
-            
+
+    .split-repo-buttons
+      = render "projects/buttons/download"
       = render 'projects/buttons/dropdown'
+
     = render 'projects/buttons/notifications'
-    
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 7b21095ea3e8637688f32cbf4ef94bf2e6f4379b..8218cf11201899929c9b2414c13b4e73b7bc2eb0 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -5,7 +5,7 @@
         %a.js-md-write-button(href="#md-write-holder" tabindex="-1")
           Write
       %li
-        %a.js-md-preview-button(href="md-preview-holder" tabindex="-1")
+        %a.js-md-preview-button(href="#md-preview-holder" tabindex="-1")
           Preview
 
     - if defined?(referenced_users) && referenced_users
diff --git a/app/views/projects/_readme.html.haml b/app/views/projects/_readme.html.haml
index b5ef0aca54035a77f2e684506350124d77561b99..d1191928d4fd36811b80baa02cccbb4c09cc6961 100644
--- a/app/views/projects/_readme.html.haml
+++ b/app/views/projects/_readme.html.haml
@@ -7,15 +7,16 @@
       = cache(readme_cache_key) do
         = render_readme(readme)
 - else
-  %h3.page-title
-    This project does not have README yet
-  - if can?(current_user, :push_code, @project)
-    %p.slead
-      A
-      %code README
-      file contains information about other files in a repository and is commonly
-      distributed with computer software, forming part of its documentation.
-      %br
-      We recommend you to
-      = link_to "add README", new_readme_path, class: 'underlined-link'
-      file to the repository and GitLab will render it here instead of this message.
+  .gray-content-block.second-block.center
+    %h3.page-title
+      This project does not have README yet
+    - if can?(current_user, :push_code, @project)
+      %p
+        A
+        %code README
+        file contains information about other files in a repository and is commonly
+        distributed with computer software, forming part of its documentation.
+      %p
+        We recommend you to
+        = link_to "add README", new_readme_path, class: 'underlined-link'
+        file to the repository and GitLab will render it here instead of this message.
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index 63ebfc9381f25bf82a40e5a9fe24904fe8d2cc99..7e6301abde82aca3767f01e850733c78b2e06113 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -2,9 +2,12 @@
   %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
   .zen-backdrop
     - classes << ' js-gfm-input markdown-area'
-    = f.text_area attr, class: classes, placeholder: ''
+    - if defined?(f) && f
+      = f.text_area attr, class: classes, placeholder: ''
+    - else
+      = text_area_tag attr, nil, class: classes, placeholder: ''
     %a.zen-enter-link(tabindex="-1" href="#")
-      %i.fa.fa-expand
+      = icon('expand')
       Edit in fullscreen
     %a.zen-leave-link(href="#")
-      %i.fa.fa-compress
+      = icon('compress')
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 6518c4173e1a0ff47bef67e37b5804b3d5cd5cfd..8d9ec068a4358a38ba030fb9665033a67d9ea643 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -6,7 +6,7 @@
 #tree-holder.tree-holder
   .file-holder
     .file-title
-      %i.fa.fa-file
+      = blob_icon @blob.mode, @blob.name
       %strong
         = @path
       %small= number_to_human_size @blob.size
@@ -43,4 +43,3 @@
                   - blame_group[:lines].each do |line|
                     :erb
                       <%= highlight(@blob.name, line, nowrap: true, continue: true).html_safe %>
-
diff --git a/app/views/projects/blob/_actions.html.haml b/app/views/projects/blob/_actions.html.haml
index 373b3a0c5b0afe674cd720fb8bf68c0d8fa6aca0..ba3e0c3c590b1759c8a68f40be3bc106adf52b98 100644
--- a/app/views/projects/blob/_actions.html.haml
+++ b/app/views/projects/blob/_actions.html.haml
@@ -19,4 +19,4 @@
 - if allowed_tree_edit?
   .btn-group{ role: "group" }
     %button.btn.btn-default{ 'data-target' => '#modal-upload-blob', 'data-toggle' => 'modal' } Replace
-    %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Remove
+    %button.btn.btn-remove{ 'data-target' => '#modal-remove-blob', 'data-toggle' => 'modal' } Delete
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index f1ad0c3c403ca45338dcace3d94e2fe0dfda7230..333f5d470ed310c771b109d9dcc2c7384355e757 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -1,19 +1,19 @@
-.file-holder.file
-  .file-title
+.file-holder.file.append-bottom-default
+  .file-title.clearfix
     .editor-ref
-      %i.fa.fa-code-fork
+      = icon('code-fork')
       = ref
     %span.editor-file-name
-      - if @path
-        %span.monospace
-          = @path
+      = @path
 
-      - if current_action?(:new) || current_action?(:create)
+    - if current_action?(:new) || current_action?(:create)
+      %span.editor-file-name
         \/
-        = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
-          required: true, class: 'form-control new-file-name js-quick-submit'
-      .pull-right
-        = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'form-control'
+      = text_field_tag 'file_name', params[:file_name], placeholder: "File name",
+        required: true, class: 'form-control new-file-name js-quick-submit'
+
+    .pull-right
+      = select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
 
   .file-content.code
     %pre.js-edit-mode-pane#editor
diff --git a/app/views/projects/blob/_new_dir.html.haml b/app/views/projects/blob/_new_dir.html.haml
index a0fc8bbd75201e077631c45ed2ce5285b798b1a8..13b5ffd17ff1c69667076a53079568cc8abe20fa 100644
--- a/app/views/projects/blob/_new_dir.html.haml
+++ b/app/views/projects/blob/_new_dir.html.haml
@@ -5,21 +5,19 @@
         %a.close{href: "#", "data-dismiss" => "modal"} 脳
         %h3.page-title Create New Directory
       .modal-body
-        = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, id: 'dir-create-form', class: 'form-horizontal' do
+        = form_tag namespace_project_create_dir_path(@project.namespace, @project, @id), method: :post, remote: false, class: 'form-horizontal js-create-dir-form' do
           .form-group
             = label_tag :dir_name, 'Directory Name', class: 'control-label'
             .col-sm-10
               = text_field_tag :dir_name, params[:dir_name], placeholder: "Directory name", required: true, class: 'form-control'
-          = render 'shared/commit_message_container', params: params, placeholder: ''
-          - unless @project.empty_repo?
-            .form-group
-              = label_tag :branch_name, 'Branch', class: 'control-label'
-              .col-sm-10
-                = text_field_tag 'new_branch', @ref, class: "form-control"
+
+          = render 'shared/new_commit_form', placeholder: "Add new directory"
+
           .form-group
             .col-sm-offset-2.col-sm-10
               = submit_tag "Create directory", class: 'btn btn-primary btn-create'
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
 :javascript
-  disableButtonIfAnyEmptyField($("#dir-create-form"), ".form-control", ".btn-create");
+  disableButtonIfAnyEmptyField($(".js-create-dir-form"), ".form-control", ".btn-create");
+  new NewCommitForm($('.js-create-dir-form'))
diff --git a/app/views/projects/blob/_remove.html.haml b/app/views/projects/blob/_remove.html.haml
index cae5ff010994f4a1b18b73513612369e49c431c2..1cf19a7d3db916b991afacb9cd684e8e3e1ddc46 100644
--- a/app/views/projects/blob/_remove.html.haml
+++ b/app/views/projects/blob/_remove.html.haml
@@ -3,16 +3,16 @@
     .modal-content
       .modal-header
         %a.close{href: "#", "data-dismiss" => "modal"} 脳
-        %h3.page-title Remove #{@blob.name}
-        %p.light
-          From branch
-          %strong= @ref
+        %h3.page-title Delete #{@blob.name}
 
       .modal-body
-        = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-requires-input' do
-          = render 'shared/commit_message_container', params: params,
-                   placeholder: 'Removed this file because...'
+        = form_tag namespace_project_blob_path(@project.namespace, @project, @id), method: :delete, class: 'form-horizontal js-replace-blob-form js-requires-input' do
+          = render 'shared/new_commit_form', placeholder: "Delete #{@blob.name}"
+
           .form-group
             .col-sm-offset-2.col-sm-10
-              = button_tag 'Remove file', class: 'btn btn-remove btn-remove-file'
+              = button_tag 'Delete file', class: 'btn btn-remove btn-remove-file'
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
+
+:javascript
+  new NewCommitForm($('.js-replace-blob-form'))
diff --git a/app/views/projects/blob/_upload.html.haml b/app/views/projects/blob/_upload.html.haml
index a1c54e731f04c2553049ca2f78740a77165cc1e9..3bb61f0c9444a90a50f32dccbb90eeaed7521d37 100644
--- a/app/views/projects/blob/_upload.html.haml
+++ b/app/views/projects/blob/_upload.html.haml
@@ -5,7 +5,7 @@
         %a.close{href: "#", "data-dismiss" => "modal"} 脳
         %h3.page-title #{title}
       .modal-body
-        = form_tag form_path, method: method, class: 'blob-file-upload-form-js form-horizontal' do
+        = form_tag form_path, method: method, class: 'js-upload-blob-form form-horizontal' do
           .dropzone
             .dropzone-previews.blob-upload-dropzone-previews
               %p.dz-message.light
@@ -13,19 +13,15 @@
                 = link_to 'click to upload', '#', class: "markdown-selector"
           %br
           .dropzone-alerts{class: "alert alert-danger data", style: "display:none"}
-          = render 'shared/commit_message_container', params: params,
-            placeholder: placeholder
-          - unless @project.empty_repo?
-            .form-group.branch
-              = label_tag 'branch', class: 'control-label' do
-                Branch
-              .col-sm-10
-                = text_field_tag 'new_branch', @ref, class: "form-control"
+
+          = render 'shared/new_commit_form', placeholder: placeholder
+
           .form-group
             .col-sm-offset-2.col-sm-10
               = button_tag button_title, class: 'btn btn-small btn-primary btn-upload-file', id: 'submit-all'
               = link_to "Cancel", '#', class: "btn btn-cancel", "data-dismiss" => "modal"
 
 :javascript
-  disableButtonIfEmptyField($('.blob-file-upload-form-js').find('#commit_message'), '.btn-upload-file');
-  new BlobFileDropzone($('.blob-file-upload-form-js'), '#{method}');
+  disableButtonIfEmptyField($('.js-upload-blob-form').find('.js-commit-message'), '.btn-upload-file');
+  new BlobFileDropzone($('.js-upload-blob-form'), '#{method}');
+  new NewCommitForm($('.js-upload-blob-form'))
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index a811adc5094803d3039e1e8742c7a9a1d3881196..a47fe7ede8059d19e7839114d90d855970b3bdb3 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -5,23 +5,17 @@
   %ul.center-top-menu.no-bottom.js-edit-mode
     %li.active
       = link_to '#editor' do
-        %i.fa.fa-edit
-        Edit file
+        = icon('edit')
+        Edit File
 
     %li
       = link_to '#preview', 'data-preview-url' => namespace_project_preview_blob_path(@project.namespace, @project, @id) do
-        %i.fa.fa-eye
+        = icon('eye')
         = editing_preview_title(@blob.name)
 
-  = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input') do
+  = form_tag(namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put, class: 'form-horizontal js-requires-input js-edit-blob-form') do
     = render 'projects/blob/editor', ref: @ref, path: @path, blob_data: @blob.data
-    = render 'shared/commit_message_container', params: params, placeholder: "Update #{@blob.name}"
-
-    .form-group.branch
-      = label_tag 'branch', class: 'control-label' do
-        Branch
-      .col-sm-10
-        = text_field_tag 'new_branch', @ref, class: "form-control"
+    = render 'shared/new_commit_form', placeholder: "Update #{@blob.name}"
 
     = hidden_field_tag 'last_commit', @last_commit
     = hidden_field_tag 'content', '', id: "file-content"
@@ -30,3 +24,4 @@
 
 :javascript
   blob = new EditBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", "#{@blob.language.try(:ace_mode)}")
+  new NewCommitForm($('.js-edit-blob-form'))
diff --git a/app/views/projects/blob/new.html.haml b/app/views/projects/blob/new.html.haml
index 7975137c37f018f20911f5c9370c474cd3deb303..1ff68005450841e4a3aeea889b633dc9fa6007d2 100644
--- a/app/views/projects/blob/new.html.haml
+++ b/app/views/projects/blob/new.html.haml
@@ -2,20 +2,13 @@
 = render "header_title"
 
 .gray-content-block.top-block
-  Create a new file
+  %h3.page-title
+    Create New File
 
 .file-editor
-  = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal form-new-file js-requires-input') do
+  = form_tag(namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post, class: 'form-horizontal js-new-blob-form js-requires-input') do
     = render 'projects/blob/editor', ref: @ref
-    = render 'shared/commit_message_container', params: params,
-             placeholder: 'Add new file'
-
-    - unless @project.empty_repo?
-      .form-group.branch
-        = label_tag 'branch', class: 'control-label' do
-          Branch
-        .col-sm-10
-          = text_field_tag 'new_branch', @ref, class: "form-control js-quick-submit"
+    = render 'shared/new_commit_form', placeholder: "Add new file"
 
     = hidden_field_tag 'content', '', id: 'file-content'
     = render 'projects/commit_button', ref: @ref,
@@ -23,3 +16,4 @@
 
 :javascript
   blob = new NewBlob(gon.relative_url_root + "#{Gitlab::Application.config.assets.prefix}", null)
+  new NewCommitForm($('.js-new-blob-form'))
diff --git a/app/views/projects/blob/show.html.haml b/app/views/projects/blob/show.html.haml
index f52b89f69210f1aff6ded38209e88dd99f782ae5..b7276868ce67da31a75007d4ee7811c74baedb8d 100644
--- a/app/views/projects/blob/show.html.haml
+++ b/app/views/projects/blob/show.html.haml
@@ -10,6 +10,4 @@
   = render 'projects/blob/remove'
 
   - title = "Replace #{@blob.name}"
-  = render 'projects/blob/upload', title: title, placeholder: title,
-    button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id),
-    method: :put
+  = render 'projects/blob/upload', title: title, placeholder: title, button_title: 'Replace file', form_path: namespace_project_update_blob_path(@project.namespace, @project, @id), method: :put
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index a4202d1120dfe6ebef70c9f7e8c91a2cae9acdfc..3dfce3c1dd64864f18dad2137a46e57832a6a197 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -30,7 +30,7 @@
           Compare
 
       - if can_remove_branch?(@project, branch.name)
-        = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: 'Removed branch cannot be restored. Are you sure?'}, remote: true do
+        = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row', method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?" }, remote: true do
           = icon("trash-o")
 
     - if branch.name != @repository.root_ref
diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml
index 22d77dda938d9713c2dfd6b50fc0a18ba4f602a1..9fe65cbb104997e6a8aad64404ddb7dd620cf53a 100644
--- a/app/views/projects/branches/_commit.html.haml
+++ b/app/views/projects/branches/_commit.html.haml
@@ -1,5 +1,5 @@
 .branch-commit
-  = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id"
+  = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id monospace"
   &middot;
   %span.str-truncated
     = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
diff --git a/app/views/projects/branches/index.html.haml b/app/views/projects/branches/index.html.haml
index 03ade02a0c802bc20698069d7a65be382f582f6f..204def6079426ffd6933849260ebcce558405a9e 100644
--- a/app/views/projects/branches/index.html.haml
+++ b/app/views/projects/branches/index.html.haml
@@ -5,7 +5,7 @@
   .pull-right
     - if can? current_user, :push_code, @project
       = link_to new_namespace_project_branch_path(@project.namespace, @project), class: 'btn btn-create' do
-        %i.fa.fa-add-sign
+        = icon('plus')
         New branch
       &nbsp;
     .dropdown.inline
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 7661452e6ec5cc140eb8f8771ad34232dc1f02c0..907e1ce10bddaf600fa7d406abad12505e164b44 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -171,7 +171,7 @@
                 %td
                   = ci_icon_for_status(build.status)
                 %td
-                  = link_to namespace_project_build_path(@project.namespace, @project, @build) do
+                  = link_to namespace_project_build_path(@project.namespace, @project, build) do
                     - if build.name
                       = build.name
                     - else
diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..14ee2263b7d8a417ed50f2bbaf18a44c39d20704
--- /dev/null
+++ b/app/views/projects/buttons/_download.html.haml
@@ -0,0 +1,4 @@
+- unless @project.empty_repo?
+  - if can? current_user, :download_code, @project
+    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', rel: 'nofollow', title: "Download ZIP" do
+      = icon('download')
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index bed2b16249e62aa888a33739f9f6344766450eda..b277b765b6b526a0c123e01823c0e9cdd9012f5a 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -5,7 +5,7 @@
     %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
       - if can?(current_user, :create_issue, @project)
         %li
-          = link_to url_for_new_issue do
+          = link_to url_for_new_issue(@project, only_path: true) do
             = icon('exclamation-circle fw')
             New issue
       - if can?(current_user, :create_merge_request, @project)
@@ -32,5 +32,3 @@
           = link_to new_namespace_project_tag_path(@project.namespace, @project) do
             = icon('tags fw')
             New tag
-
-
diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml
index 8f2f631eb7d24eeb1ea50f87672ae5d2da46efc9..2d3abf09051852a7bab10bf71923ce8726e86614 100644
--- a/app/views/projects/buttons/_fork.html.haml
+++ b/app/views/projects/buttons/_fork.html.haml
@@ -1,12 +1,13 @@
-- if current_user && can?(current_user, :fork_project, @project)
-  - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
-    = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn' do
-      = icon('code-fork fw')
-      Fork
-      %span.count
-        = @project.forks_count
-  - else
-    = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn' do
-      = icon('code-fork fw')
-      %span.count
-        = @project.forks_count
+- unless @project.empty_repo?
+  - if current_user && can?(current_user, :fork_project, @project)
+    - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2
+      = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do
+        = icon('code-fork fw')
+        Fork
+        %span.count
+          = @project.forks_count
+    - else
+      = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do
+        = icon('code-fork fw')
+        %span.count
+          = @project.forks_count
diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml
index 06583902035de013d505250661d194a988b7ffe1..41a3ec6d90fafbed9a389e5cd59486ad678c4837 100644
--- a/app/views/projects/buttons/_star.html.haml
+++ b/app/views/projects/buttons/_star.html.haml
@@ -1,5 +1,5 @@
 - if current_user
-  = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star', method: :post, remote: true do
+  = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do
     = icon('star fw')
     %span.count
       = @project.star_count
diff --git a/app/views/projects/ci_settings/edit.html.haml b/app/views/projects/ci_settings/edit.html.haml
index 665556f5c204c6404202f102c0c6f8332423d4f8..acc912d459606c5831bf20246ad1f8c2eaeefe2a 100644
--- a/app/views/projects/ci_settings/edit.html.haml
+++ b/app/views/projects/ci_settings/edit.html.haml
@@ -1,25 +1,6 @@
 - page_title "CI Settings"
-- if @ci_project.generated_yaml_config
-  %p.alert.alert-danger
-    CI Jobs are deprecated now, you can #{link_to "download", dumped_yaml_ci_project_path(@ci_project)}
-    or
-    %a.preview-yml{:href => "#yaml-content", "data-toggle" => "modal"} preview
-    yaml file which is based on your old jobs.
-    Put this file to the root of your project and name it .gitlab-ci.yml
 
 - if no_runners_for_project?(@ci_project)
   = render 'no_runners'
 
 = render 'form'
-
-- if @ci_project.generated_yaml_config
-  #yaml-content.modal.fade{"aria-hidden" => "true", "aria-labelledby" => ".gitlab-ci.yml", :role => "dialog", :tabindex => "-1"}
-    .modal-dialog
-      .modal-content
-        .modal-header
-          %button.close{"aria-hidden" => "true", "data-dismiss" => "modal", :type => "button"} 脳
-          %h4.modal-title Content of .gitlab-ci.yml
-        .modal-body
-          = text_area_tag :yaml, @ci_project.generated_yaml_config, size: "70x25", class: "form-control"
-        .modal-footer
-          %button.btn.btn-default{"data-dismiss" => "modal", :type => "button"} Close
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index c73ba74f5efc3a1c15ce09492cd7a40c42d86385..76dc87a8824e5952a47191b9c3e96ef37319fa2c 100644
--- a/app/views/projects/commit/_ci_menu.html.haml
+++ b/app/views/projects/commit/_ci_menu.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu.commit-ci-menu
+%ul.center-top-menu.no-top.no-bottom.commit-ci-menu
   = nav_link(path: 'commit#show') do
     = link_to namespace_project_commit_path(@project.namespace, @project, @commit.id) do
       Changes
diff --git a/app/views/projects/commit/_commit_box.html.haml b/app/views/projects/commit/_commit_box.html.haml
index 776768537d0ac9efb4efa6ff93fc2c9e7c890209..d8bfe6a07ac6ec2d90f890e0390185262dca1c1f 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -13,8 +13,9 @@
         - unless @commit.parents.length > 1
           %li= link_to "Email Patches", namespace_project_commit_path(@project.namespace, @project, @commit, format: :patch)
         %li= link_to "Plain Diff",    namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff)
-    = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-primary btn-grouped" do
-      %span Browse Code 禄
+    = link_to namespace_project_tree_path(@project.namespace, @project, @commit), class: "btn btn-grouped" do
+      = icon('files-o')
+      Browse Files
   %div
 
 %p
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index 30a3973828ff39cfd7139a8ab5f3899c539d5c64..069b8b1f1696b897f4b36fd72e7d0109fdefc4f6 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -1,6 +1,9 @@
 - page_title "#{@commit.title} (#{@commit.short_id})", "Commits"
 = render "projects/commits/header_title"
 = render "commit_box"
-= render "ci_menu" if @ci_commit
+- if @ci_commit
+  = render "ci_menu"
+- else
+  %div.block-connector
 = render "projects/diffs/diffs", diffs: @diffs, project: @project
-= render "projects/notes/notes_with_form", view: params[:view]
+= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 805be332e64510002ec7d9f7a754fe4a0a78387c..2e489a0a4d5b6646b105e7948b75349a6279a411 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -20,8 +20,8 @@
         - if ci_commit
           = render_ci_status(ci_commit)
           &nbsp;
-        = clipboard_button
-        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id", data: {clipboard_text: commit.id}
+        = clipboard_button(clipboard_text: commit.id)
+        = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit_short_id"
 
       .notes_count
         - if note_count > 0
diff --git a/app/views/projects/commits/show.atom.builder b/app/views/projects/commits/show.atom.builder
index 3854ad5d6116890bacfdfcd67ecb2e870acc73f0..268b9b815ee89b8197abec3a523df41c7c6f6619 100644
--- a/app/views/projects/commits/show.atom.builder
+++ b/app/views/projects/commits/show.atom.builder
@@ -12,7 +12,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
       xml.link    href: namespace_project_commit_url(@project.namespace, @project, id: commit.id)
       xml.title   truncate(commit.title, length: 80)
       xml.updated commit.committed_date.strftime("%Y-%m-%dT%H:%M:%SZ")
-      xml.media   :thumbnail, width: "40", height: "40", url: avatar_icon(commit.author_email)
+      xml.media   :thumbnail, width: "40", height: "40", url: image_url(avatar_icon(commit.author_email))
       xml.author do |author|
         xml.name commit.author_name
         xml.email commit.author_email
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 39755efd2fda2e9b326a7f6a4eb3a3026d1be333..51088a7dea89b0e97622dde7a1b0ba641ea8c0d8 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -7,11 +7,11 @@
   = render "form"
 
 - if @commits.present?
-  .prepend-top-20
+  .prepend-top-default
     = render "projects/commits/commit_list"
     = render "projects/diffs/diffs", diffs: @diffs, project: @project
 - else
-  .light-well.prepend-top-20
+  .light-well.prepend-top-default
     .center
       %h4
         There isn't anything to compare.
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index e46bf1ab1e7c51a81b95c8084559d7924001f84e..f9d661d59d291b83a15d7ed2a43cfb1f0bce868d 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -1,9 +1,9 @@
-- if params[:view] == 'parallel'
+- if diff_view == 'parallel'
   - fluid_layout true
 
 - diff_files = safe_diff_files(diffs)
 
-.gray-content-block.second-block.oneline-block
+.gray-content-block.middle-block.oneline-block
   .inline-parallel-buttons
     .btn-group
       = inline_diff_btn
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 410ff6abb43f1f892f1560ed853a8ed65f88efe7..b3392d00e0110274889d3f8cc5063bd3eab101f4 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -2,19 +2,27 @@
   .diff-header{id: "file-path-#{hexdigest(diff_file.file_path)}"}
     - if diff_file.diff.submodule?
       %span
+        = icon('archive fw')
         - submodule_item = project.repository.blob_at(@commit.id, diff_file.file_path)
-        = submodule_link(submodule_item, @commit.id, project.repository)
+        %strong
+          = submodule_link(submodule_item, @commit.id, project.repository)
     - else
       %span
+        = blob_icon blob.mode, blob.name
+        = link_to "#diff-#{i}" do
+          %strong
+            = diff_file.new_path
+
         - if diff_file.deleted_file
-          = "#{diff_file.old_path} deleted"
+          deleted
         - elsif diff_file.renamed_file
-          = "#{diff_file.old_path} renamed to #{diff_file.new_path}"
-        - else
-          = diff_file.new_path
+          renamed from
+          %strong
+            = diff_file.old_path
 
         - if diff_file.mode_changed?
-          %span.file-mode= "#{diff_file.diff.a_mode} 鈫� #{diff_file.diff.b_mode}"
+          %small
+            = "#{diff_file.diff.a_mode} 鈫� #{diff_file.diff.b_mode}"
 
       .diff-controls
         - if blob.text?
@@ -33,7 +41,7 @@
     -# Skipp all non non-supported blobs
     - return unless blob.respond_to?('text?')
     - if blob.text?
-      - if params[:view] == 'parallel'
+      - if diff_view == 'parallel'
         = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
       - else
         = render "projects/diffs/text_file", diff_file: diff_file, index: i
@@ -42,4 +50,3 @@
       = render "projects/diffs/image", diff_file: diff_file, old_file: old_file, file: blob, index: i
     - else
       .nothing-here-block No preview for this file type
-
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index afbf88b55073ed9bdcb37b348c3d345e5b18aa80..0c10de1604c75521c3494db89c94e10057b66294 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -35,7 +35,7 @@
           .form-group
             = f.label :tag_list, "Tags", class: 'control-label'
             .col-sm-10
-              = f.text_field :tag_list, maxlength: 2000, class: "form-control"
+              = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control"
               %p.help-block Separate tags with commas.
 
           %fieldset.features
@@ -57,7 +57,16 @@
                     = f.check_box :merge_requests_enabled
                     %strong Merge Requests
                     %br
-                    %span.descr Submit changes to be merged upstream.
+                    %span.descr Submit changes to be merged upstream
+
+            .form-group
+              .col-sm-offset-2.col-sm-10
+                .checkbox
+                  = f.label :builds_enabled do
+                    = f.check_box :builds_enabled
+                    %strong Builds
+                    %br
+                    %span.descr Test and deploy your changes before merge
 
             .form-group
               .col-sm-offset-2.col-sm-10
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index c3858e78caddf4e44fc1e79a837bad28b4029e33..950ab33825e82d5785c9875ad5cf5769ec3fd683 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -5,17 +5,16 @@
 
 = render "home_panel"
 
-.gray-content-block.center
+.gray-content-block.second-block.center
   %h3.page-title
     The repository for this project is empty
-  - if can?(current_user, :download_code, @project)
+  - if can?(current_user, :push_code, @project)
     %p
       If you already have files you can push them using command line instructions below.
-      %br
-      - if can?(current_user, :push_code, @project)
-        Otherwise you can start with
-        = link_to "adding README", new_readme_path, class: 'underlined-link'
-        file to this project.
+    %p
+      Otherwise you can start with
+      = link_to "adding README", new_readme_path, class: 'underlined-link'
+      file to this project.
 
 - if can?(current_user, :download_code, @project)
   .prepend-top-20
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index e0d06a14bf44ee79d39d885ac4a21a341217c11d..03d0733f913268357e05805f9fc0ebba6b76aef7 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -3,7 +3,7 @@
     = link_to 'Contributors', namespace_project_graph_path
   = nav_link(action: :commits) do
     = link_to 'Commits', commits_namespace_project_graph_path
-  - if @project.gitlab_ci?
+  - if @project.builds_enabled?
     = nav_link(action: :ci) do
       = link_to ci_namespace_project_graph_path do
         Continuous Integration
diff --git a/app/views/projects/imports/new.html.haml b/app/views/projects/imports/new.html.haml
index 92a87690c54992961bdb9bc97b72d39ff97d998b..6027fb23360ffb3904e81f42112442b0438ec6fb 100644
--- a/app/views/projects/imports/new.html.haml
+++ b/app/views/projects/imports/new.html.haml
@@ -1,22 +1,19 @@
 - page_title "Import repository"
 %h3.page-title
-  - if @project.import_failed?
-    Import failed. Retry?
-  - else
-    Import repository
+  Import repository
 
 %hr
 
+- if @project.import_failed?
+  .panel.panel-danger
+    .panel-heading The repository could not be imported.
+    .panel-body
+      %pre
+        :preserve
+          #{@project.import_error.try(:strip)}
+
 = form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f|
-  .form-group.import-url-data
-    = f.label :import_url, class: 'control-label' do
-      %span Import existing git repo
-    .col-sm-10
-      = f.text_field :import_url, class: 'form-control', placeholder: 'https://github.com/randx/six.git'
-      .well.prepend-top-20
-        This URL must be publicly accessible or you can add a username and password like this: https://username:password@gitlab.com/company/project.git.
-        %br
-        The import will time out after 4 minutes. For big repositories, use a clone/push combination.
-        For SVN repositories, check #{link_to "this migrating from SVN doc.", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}
+  = render "shared/import_form", f: f
+
   .form-actions
     = f.submit 'Start import', class: "btn btn-create", tabindex: 4
diff --git a/app/views/projects/imports/show.html.haml b/app/views/projects/imports/show.html.haml
index 06886d215a3c432a764d872819f08d9cc22d08e5..c0d1ce0d120a6887bc11a652bd082e69d062e784 100644
--- a/app/views/projects/imports/show.html.haml
+++ b/app/views/projects/imports/show.html.haml
@@ -8,7 +8,7 @@
       - else
         Import in progress.
     - unless @project.forked?
-      %p.monospace git clone --bare #{hidden_pass_url(@project.import_url)}
+      %p.monospace git clone --bare #{@project.safe_import_url}
     %p Please wait while we import the repository for you. Refresh at will.
     :javascript
       new ProjectImport();
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index c5fd863ae99eacf0ed5c83eb2e27fb3eb28527be..8f0a1ed9be2f3ede2e47fe7a3e1a0991921ef42a 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -7,7 +7,7 @@
 
 = render 'shared/show_aside'
 
-.gray-content-block.second-block
+.gray-content-block.second-block.oneline-block
   .row
     .col-md-9
       .votes-holder.pull-right
@@ -18,9 +18,9 @@
           = link_to_member(@project, participant, name: false, size: 24)
     .col-md-3
       .input-group.cross-project-reference
-        %span.slead.has_tooltip{title: 'Cross-project reference'}
+        %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
           = cross_project_reference(@project, @issue)
-        = clipboard_button
+        = clipboard_button(clipboard_target: '#cross-project-reference')
 
 .row
   %section.col-md-9
diff --git a/app/views/projects/issues/_issue.html.haml b/app/views/projects/issues/_issue.html.haml
index 55ce912829ddee5c3deb50f95904cf34207d0d6d..1eb71990e55d4e8ad1ef5b162eb1cfcd0d3070e1 100644
--- a/app/views/projects/issues/_issue.html.haml
+++ b/app/views/projects/issues/_issue.html.haml
@@ -6,37 +6,39 @@
   .issue-title
     %span.issue-title-text
       = link_to_gfm issue.title, issue_path(issue), class: "row_title"
-    .issue-labels
-      - issue.labels.each do |label|
-        = link_to_label(label, project: issue.project)
     .pull-right.light
       - if issue.closed?
         %span
           CLOSED
       - if issue.assignee
-        = link_to_member(@project, issue.assignee, name: false)
+        = link_to_member(@project, issue.assignee, name: false, title: "Assigned to :name")
       - note_count = issue.notes.user.count
       - if note_count > 0
         &nbsp;
-        %span
-          %i.fa.fa-comments
+        = link_to issue_path(issue) + "#notes" do
+          = icon('comments')
           = note_count
       - else
         &nbsp;
-        %span.issue-no-comments
-          %i.fa.fa-comments
+        = link_to issue_path(issue) + "#notes", class: "issue-no-comments" do
+          = icon('comments')
           = 0
 
   .issue-info
-    = "#{issue.to_reference} opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')} by #{link_to_member(@project, issue.author, avatar: false)}".html_safe
-    - if issue.votes_count > 0
-      = render 'votes/votes_inline', votable: issue
+    #{issue.to_reference} &middot;
+    opened #{time_ago_with_tooltip(issue.created_at, placement: 'bottom')}
+    by #{link_to_member(@project, issue.author, avatar: false)}
     - if issue.milestone
       &nbsp;
-      %span
-        %i.fa.fa-clock-o
+      = link_to namespace_project_issues_path(issue.project.namespace, issue.project, milestone_title: issue.milestone.title) do
+        = icon('clock-o')
         = issue.milestone.title
+    - if issue.labels.any?
+      &nbsp;
+      - issue.labels.each do |label|
+        = link_to_label(label, project: issue.project)
     - if issue.tasks?
+      &nbsp;
       %span.task-status
         = issue.task_status
 
diff --git a/app/views/projects/labels/_label.html.haml b/app/views/projects/labels/_label.html.haml
index c6ebfa281a149f9e5ba4eaf8e451c1718ef5d8ea..b70a9fc9fe50ecf57526eb0c63a362fdac45568d 100644
--- a/app/views/projects/labels/_label.html.haml
+++ b/app/views/projects/labels/_label.html.haml
@@ -7,4 +7,4 @@
 
     - if can? current_user, :admin_label, @project
       = link_to 'Edit', edit_namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm'
-      = link_to 'Remove', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
+      = link_to 'Delete', namespace_project_label_path(@project.namespace, @project, label), class: 'btn btn-sm btn-remove remove-row', method: :delete, remote: true, data: {confirm: "Remove this label? Are you sure?"}
diff --git a/app/views/projects/labels/index.html.haml b/app/views/projects/labels/index.html.haml
index fb784ee5f4f4f686432ca1cf62c6f8b88d71d0ec..9081bcfe9b35ae7a899c2f1ffe3b7b3083cc490a 100644
--- a/app/views/projects/labels/index.html.haml
+++ b/app/views/projects/labels/index.html.haml
@@ -4,6 +4,7 @@
 .gray-content-block.top-block
   - if can? current_user, :admin_label, @project
     = link_to new_namespace_project_label_path(@project.namespace, @project), class: "pull-right btn btn-new" do
+      = icon('plus')
       New label
   .oneline
     Labels can be applied to issues and merge requests.
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 7e60782ff5b699cb8d70ea5d68a147a5909fd006..4a192aeb2cd5a69b375a4025094935b1cfbf7590 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -7,15 +7,17 @@
 
 = render 'shared/show_aside'
 
-.gray-content-block.second-block.oneline-block
+.gray-content-block.middle-block.oneline-block
   .row
     .col-md-9
       .votes-holder.pull-right
         #votes= render 'votes/votes_block', votable: @merge_request
       = render "projects/merge_requests/show/participants"
     .col-md-3
-      %span.slead.has_tooltip{:"data-original-title" => 'Cross-project reference'}
-        = cross_project_reference(@project, @merge_request)
+      .input-group.cross-project-reference
+        %span#cross-project-reference.slead.has_tooltip{title: 'Cross-project reference'}
+          = cross_project_reference(@project, @merge_request)
+        = clipboard_button(clipboard_target: '#cross-project-reference')
 
 .row
   %section.col-md-9
diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml
index c5234c0618c4d06250ad6b6032cae58cb8bcb0c0..1d4c9b66c429c259f3c28531d92218508ad72d55 100644
--- a/app/views/projects/merge_requests/_merge_request.html.haml
+++ b/app/views/projects/merge_requests/_merge_request.html.haml
@@ -3,50 +3,52 @@
   .merge-request-title
     %span.merge-request-title-text
       = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
-    .merge-request-labels
-      - merge_request.labels.each do |label|
-        = link_to_label(label, project: merge_request.project)
     .pull-right.light
       - if ci_commit
         = render_ci_status(ci_commit)
       - if merge_request.merged?
         %span
-          %i.fa.fa-check
+          = icon('check')
           MERGED
       - elsif merge_request.closed?
         %span
-          %i.fa.fa-ban
+          = icon('ban')
           CLOSED
       - note_count = merge_request.mr_and_commit_notes.user.count
       - if merge_request.assignee
         &nbsp;
-        = link_to_member(merge_request.source_project, merge_request.assignee, name: false)
+        = link_to_member(merge_request.source_project, merge_request.assignee, name: false, title: "Assigned to :name")
       - if note_count > 0
         &nbsp;
-        %span
-          %i.fa.fa-comments
+        = link_to merge_request_path(merge_request) + "#notes" do
+          = icon('comments')
           = note_count
       - else
         &nbsp;
-        %span.merge-request-no-comments
-          %i.fa.fa-comments
+        = link_to merge_request_path(merge_request) + "#notes", class: "merge-request-no-comments" do
+          = icon('comments')
           = 0
 
   .merge-request-info
-    = "##{merge_request.iid} opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')} by #{link_to_member(@project, merge_request.author, avatar: false)}".html_safe
-    - if merge_request.votes_count > 0
-      = render 'votes/votes_inline', votable: merge_request
-    - if merge_request.milestone_id?
-      &nbsp;
-      %span
-        %i.fa.fa-clock-o
-        = merge_request.milestone.title
+    \##{merge_request.iid} &middot;
+    opened #{time_ago_with_tooltip(merge_request.created_at, placement: 'bottom')}
+    by #{link_to_member(@project, merge_request.author, avatar: false)}
     - if merge_request.target_project.default_branch != merge_request.target_branch
       &nbsp;
-      %span
-        %i.fa.fa-code-fork
+      = link_to namespace_project_commits_path(merge_request.project.namespace, merge_request.project, merge_request.target_branch) do
+        = icon('code-fork')
         = merge_request.target_branch
+    - if merge_request.milestone
+      &nbsp;
+      = link_to namespace_project_merge_requests_path(merge_request.project.namespace, merge_request.project, milestone_title: merge_request.milestone.title) do
+        = icon('clock-o')
+        = merge_request.milestone.title
+    - if merge_request.labels.any?
+      &nbsp;
+      - merge_request.labels.each do |label|
+        = link_to_label(label, project: merge_request.project)
     - if merge_request.tasks?
+      &nbsp;
       %span.task-status
         = merge_request.task_status
 
diff --git a/app/views/projects/merge_requests/_new_compare.html.haml b/app/views/projects/merge_requests/_new_compare.html.haml
index d9eff1f9320aeaeb0a779cc4fad575158ca4d71e..6def9d6266fe5fb935f44f34842c6fe5bed84b50 100644
--- a/app/views/projects/merge_requests/_new_compare.html.haml
+++ b/app/views/projects/merge_requests/_new_compare.html.haml
@@ -37,7 +37,7 @@
         %h4 Compare failed
         %p We can't compare selected branches. It may be because of huge diff. Please try again or select different branches.
     - else
-      .light-well.append-bottom-10
+      .light-well.append-bottom-default
         .center
           %h4
             There isn't anything to merge.
@@ -86,4 +86,3 @@
       return;
     }
   });
-
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index 6244d3ba0b440c52d943a2cb7d1175d34deb9011..72132344c88ef4dab0c4d0c0748b017b9710082f 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -19,7 +19,7 @@
     = f.hidden_field :target_branch
 
 .mr-compare.merge-request
-  %ul.merge-request-tabs
+  %ul.merge-request-tabs.center-top-menu.no-top.no-bottom
     %li.commits-tab
       = link_to url_for(params), data: {target: '#commits', action: 'commits', toggle: 'tab'} do
         Commits
@@ -31,7 +31,7 @@
 
   .tab-content
     #commits.commits.tab-pane
-      = render "projects/commits/commits", project: @project
+      = render "projects/merge_requests/show/commits"
     #diffs.diffs.tab-pane.active
       - if @diffs.present?
         = render "projects/diffs/diffs", diffs: @diffs, project: @project
@@ -57,4 +57,3 @@
     diffs_loaded: true,
     commits_loaded: true
   });
-
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index eeaa72ed21bebf3e15a4dc7ea50d3abbcfec3e02..e7eb00665944ac10c48fcd710f6e667e72c42b38 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -8,7 +8,7 @@
   .merge-request-details.issuable-details
     = render "projects/merge_requests/show/mr_title"
     = render "projects/merge_requests/show/mr_box"
-    .append-bottom-20.mr-source-target.prepend-top-default
+    .append-bottom-default.mr-source-target.prepend-top-default
       - if @merge_request.open?
         .pull-right
           - if @merge_request.source_branch_exists?
@@ -40,7 +40,7 @@
           = link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
 
   - if @commits.present?
-    %ul.merge-request-tabs
+    %ul.merge-request-tabs.center-top-menu.no-top.no-bottom
       %li.notes-tab
         = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#notes', action: 'notes', toggle: 'tab'} do
           Discussion
diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml
index 478054db517a5d3eaf2530608f769104fb58adc6..7f904ec42a096e015c4e80d0522015272279f6a9 100644
--- a/app/views/projects/merge_requests/show/_commits.html.haml
+++ b/app/views/projects/merge_requests/show/_commits.html.haml
@@ -1,4 +1,4 @@
-.gray-content-block.second-block.oneline-block
+.gray-content-block.middle-block.oneline-block
   = icon("sort-amount-desc")
   Most recent commits displayed first
 
diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index 626970f39be61a083b87ef44a7522bef57255e72..d9cfc3d7ae943ba9df5f8afa7140a76e1463ec17 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -1,5 +1,5 @@
 - if @merge_request_diff.collected?
-  = render "projects/diffs/diffs", diffs: @merge_request.diffs, project: @merge_request.project
+  = render "projects/diffs/diffs", diffs: params[:w] == '1' ? @merge_request.diffs_no_whitespace : @merge_request.diffs, project: @merge_request.project
 - elsif @merge_request_diff.empty?
   .nothing-here-block Nothing to merge from #{@merge_request.source_branch} into #{@merge_request.target_branch}
 - else
diff --git a/app/views/projects/milestones/_form.html.haml b/app/views/projects/milestones/_form.html.haml
index 255ddab479f4b3765eb37d15930540c8e35d2c20..24879b19d2bfc7a3aa7bdf46919da0d8f9cf110e 100644
--- a/app/views/projects/milestones/_form.html.haml
+++ b/app/views/projects/milestones/_form.html.haml
@@ -23,9 +23,7 @@
         .col-sm-10
           = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
             = render 'projects/zen', f: f, attr: :description, classes: 'description form-control js-quick-submit'
-            .hint
-              .pull-left Milestones are parsed with #{link_to "GitLab Flavored Markdown", help_page_path("markdown", "markdown"), target: '_blank'}.
-              .pull-left Attach files by dragging & dropping or #{link_to "selecting them", '#', class: 'markdown-selector' }.
+            = render 'projects/notes/hints'
           .clearfix
           .error-alert
     .col-md-6
@@ -45,7 +43,7 @@
 
 
 :javascript
-  $( ".datepicker" ).datepicker({
+  $(".datepicker").datepicker({
     dateFormat: "yy-mm-dd",
     onSelect: function(dateText, inst) { $("#milestone_due_date").val(dateText) }
   }).datepicker("setDate", $.datepicker.parseDate('yy-mm-dd', $('#milestone_due_date').val()));
diff --git a/app/views/projects/milestones/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index 5e93d55b1fbb3e617d0f6da1e046a869644b3dd1..334172b976f5da2958d95d0b2d6d74b3232a8edc 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -31,4 +31,4 @@
         = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, milestone, milestone: {state_event: :close }), method: :put, remote: true, class: "btn btn-xs btn-close"
         = link_to namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-xs btn-remove" do
           %i.fa.fa-trash-o
-          Remove
+          Delete
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 3a898dfbcfd1f2406da96d2afc8e7c71e4cfd77d..c3bda794c65b20d2cc6351f7c56307e78e216a93 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -1,46 +1,50 @@
 - page_title @milestone.title, "Milestones"
 = render "header_title"
 
-%h4.page-title
-  .issue-box{ class: issue_box_class(@milestone) }
-    - if @milestone.closed?
-      Closed
-    - elsif @milestone.expired?
-      Expired
-    - else
-      Open
-  Milestone ##{@milestone.iid}
-  %small.creator
-    = @milestone.expires_at
-  .pull-right
-    - if can?(current_user, :admin_milestone, @project)
-      = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
-        %i.fa.fa-pencil-square-o
-        Edit
-      - if @milestone.active?
-        = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+.issuable-details
+  .page-title
+    .issue-box{ class: issue_box_class(@milestone) }
+      - if @milestone.closed?
+        Closed
+      - elsif @milestone.expired?
+        Expired
       - else
-        = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
-      = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
-        %i.fa.fa-trash-o
-        Remove
+        Open
+    Milestone ##{@milestone.iid}
+    - if @milestone.expires_at
+      %span.creator
+        &middot;
+        = @milestone.expires_at
+    .pull-right
+      - if can?(current_user, :admin_milestone, @project)
+        = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
+          %i.fa.fa-pencil-square-o
+          Edit
+
+        - if @milestone.active?
+          = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-grouped"
+        - else
+          = link_to 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-grouped"
+
+        = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-remove" do
+          %i.fa.fa-trash-o
+          Delete
+
+  .gray-content-block.middle-block
+    %h2.issue-title
+      = gfm escape_once(@milestone.title)
+    %div
+      - if @milestone.description.present?
+        .description
+          .wiki
+            = preserve do
+              = markdown @milestone.description
 
-%hr
 - if @milestone.issues.any? && @milestone.can_be_closed?
-  .alert.alert-success
+  .alert.alert-success.prepend-top-default
     %span All issues for this milestone are closed. You may close milestone now.
 
-%h3.issue-title
-  = gfm escape_once(@milestone.title)
-%div
-  - if @milestone.description.present?
-    .description
-      .wiki
-        = preserve do
-          = markdown @milestone.description
-
-%hr
-.context
+.context.prepend-top-default
   %p.lead
     Progress:
     #{@milestone.closed_items_count} closed
@@ -51,8 +55,7 @@
     %span.pull-right= @milestone.expires_at
   = milestone_progress_bar(@milestone)
 
-
-%ul.nav.nav-tabs
+%ul.center-top-menu.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
@@ -66,17 +69,21 @@
       Participants
       %span.badge= @users.count
 
-  .pull-right
-    - if can?(current_user, :create_issue, @project)
-      = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn  btn-grouped", title: "New Issue" do
-        %i.fa.fa-plus
-        New Issue
-    - if can?(current_user, :read_issue, @project)
-      = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn  edit-milestone-link btn-grouped"
-
 .tab-content
   .tab-pane.active#tab-issues
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        - if can?(current_user, :create_issue, @project)
+          = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn  btn-grouped", title: "New Issue" do
+            %i.fa.fa-plus
+            New Issue
+        - if can?(current_user, :read_issue, @project)
+          = link_to 'Browse Issues', namespace_project_issues_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All issues in this milestone
+
+    .row.prepend-top-default
       .col-md-4
         = render('issues', title: 'Unstarted Issues (open and unassigned)', issues: @issues.opened.unassigned, id: 'unassigned')
       .col-md-4
@@ -85,7 +92,15 @@
         = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed')
 
   .tab-pane#tab-merge-requests
-    .row
+    .gray-content-block.middle-block
+      .pull-right
+        - if can?(current_user, :read_merge_request, @project)
+          = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped"
+
+      .oneline
+        All merge requests in this milestone
+
+    .row.prepend-top-default
       .col-md-3
         = render('merge_requests', title: 'Work in progress (open and unassigned)', merge_requests: @merge_requests.opened.unassigned, id: 'unassigned')
       .col-md-3
@@ -100,6 +115,10 @@
               = render 'merge_request', merge_request: merge_request
 
   .tab-pane#tab-participants
+    .gray-content-block.middle-block
+      .oneline
+        All participants to this milestone
+
     %ul.bordered-list
       - @users.each do |user|
         %li
diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml
index a02c12f06a8763bd71ecba7facc92a6b28aaa424..a4bd8d54af2776e31acf32919ad776ba11763280 100644
--- a/app/views/projects/new.html.haml
+++ b/app/views/projects/new.html.haml
@@ -11,19 +11,23 @@
           Project path
         .col-sm-10
           .input-group
-            = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 1, autofocus: true, required: true
-            .input-group-addon
-              \.git
-
-      - if current_user.can_select_namespace?
-        .form-group
-          = f.label :namespace_id, class: 'control-label' do
-            %span Namespace
-          .col-sm-10
-            = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user), {}, {class: 'select2', tabindex: 2}
+            - if current_user.can_select_namespace?
+              .input-group-addon
+                = root_url
+              = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2', tabindex: 1}
+              .input-group-addon
+                \/
+            - else
+              .input-group-addon
+                #{root_url}#{current_user.username}/
+            = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true
+
+          - if current_user.can_create_group?
+            .help-block
+              Want to house several dependent projects under the same namespace?
+              = link_to "Create a group", new_group_path
 
       - if import_sources_enabled?
-
         .project-import.js-toggle-container
           .form-group
             %label.control-label Import project from
@@ -82,19 +86,7 @@
                   %span Any repo by URL
 
           .js-toggle-content.hide
-            .form-group.import-url-data
-              = f.label :import_url, class: 'control-label' do
-                %span Git repository URL
-              .col-sm-10
-                = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
-                .well.prepend-top-20
-                  %ul
-                    %li
-                      The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
-                    %li
-                      The import will time out after 4 minutes. For big repositories, use a clone/push combination.
-                    %li
-                      To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}.
+            = render "shared/import_form", f: f
 
       .prepend-botton-10
 
@@ -109,14 +101,6 @@
       .form-actions
         = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4
 
-        - if current_user.can_create_group?
-          .pull-right
-            .light.inline
-              .space-right
-                Need a group for several dependent projects?
-            = link_to new_group_path, class: "btn" do
-              Create a group
-
 .save-project-loader.hide
   .center
     %h2
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index 13dfa0a1bb33fc48bda63410892bfb1fa2b998cb..5dd84317e3b56102978c3601a69986713869b8b5 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -1,5 +1,5 @@
 = form_for [@project.namespace.becomes(Namespace), @project, @note], remote: true, html: { :'data-type' => 'json', multipart: true, id: nil, class: "new_note js-new-note-form common-note-form gfm-form" }, authenticity_token: true do |f|
-  = hidden_field_tag :view, params[:view]
+  = hidden_field_tag :view, diff_view
   = hidden_field_tag :line_type
   = note_target_fields(@note)
   = f.hidden_field :commit_id
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index 5d184730796f9112b6f4db44967b0aa934a089ba..dd0abc8c74681047c18d08761d7f027f0c60852e 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -2,7 +2,7 @@
   .timeline-entry-inner
     .timeline-icon
       %a{href: user_path(note.author)}
-        %img.avatar.s40{src: avatar_icon(note.author), alt: ''}
+        = image_tag avatar_icon(note.author), alt: '', class: 'avatar s40'
     .timeline-content
       .note-header
         - if note_editable?(note)
@@ -35,32 +35,11 @@
               - if note.updated_by && note.updated_by != note.author
                 by #{link_to_member(note.project, note.updated_by, avatar: false, author_class: nil)}
 
-        - if note.superceded?(@notes)
-          - if note.upvote?
-            %span.vote.upvote.label.label-gray.strikethrough
-              = icon('thumbs-up')
-              \+1
-          - if note.downvote?
-            %span.vote.downvote.label.label-gray.strikethrough
-              = icon('thumbs-down')
-              \-1
-        - else
-          - if note.upvote?
-            %span.vote.upvote.label.label-success
-              = icon('thumbs-up')
-              \+1
-          - if note.downvote?
-            %span.vote.downvote.label.label-danger
-              = icon('thumbs-down')
-              \-1
-
-
       .note-body{class: note_editable?(note) ? 'js-task-list-container' : ''}
         .note-text
           = preserve do
             = markdown(note.note, {no_header_anchors: true})
-        - unless note.system?
-          -# System notes can't be edited
+        - if note_editable?(note)
           = render 'projects/notes/edit_form', note: note
 
       - if note.attachment.url
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 04222b8f7c47e10d7e2d3922c3235a6fd6c2bde9..99c1b0fa43eb8135cd5c572d4d8710e17a9e1815 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -4,7 +4,7 @@
 
 .js-main-target-form
 - if can? current_user, :create_note, @project
-  = render "projects/notes/form", view: params[:view]
+  = render "projects/notes/form", view: diff_view
 
 :javascript
-  new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{params[:view]}")
+  new Notes("#{namespace_project_notes_path(namespace_id: @project.namespace, target_id: @noteable.id, target_type: @noteable.class.name.underscore)}", #{@notes.map(&:id).to_json}, #{Time.now.to_i}, "#{diff_view}")
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index 43e92437cf577e64163efd308ca0526e86c3f1ac..0c73d7e34acf7e8c15cbea20435b5d535b74e18a 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -4,11 +4,11 @@
     group members
     %small
       (#{members.count})
-    .panel-head-actions
-      = link_to group_group_members_path(@group), class: 'btn btn-sm' do
-        %i.fa.fa-pencil-square-o
+    .pull-right
+      = link_to group_group_members_path(@group), class: 'btn' do
+        = icon('pencil-square-o')
         Edit group members
-  %ul.well-list
+  %ul.content-list
     - members.each do |member|
       = render 'groups/group_members/group_member', member: member, show_controls: false
     - if members.count > 20
diff --git a/app/views/projects/project_members/_project_member.html.haml b/app/views/projects/project_members/_project_member.html.haml
index 76c46d1d8067a7fe7dc855fa09a342c40260dbc6..05bf3a7ef6a12ee2a2265ba519ea32b4e636e9f3 100644
--- a/app/views/projects/project_members/_project_member.html.haml
+++ b/app/views/projects/project_members/_project_member.html.haml
@@ -4,7 +4,7 @@
 %li{class: "#{dom_class(member)} js-toggle-container project_member_row access-#{member.human_access.downcase}", id: dom_id(member)}
   %span.list-item-name
     - if member.user
-      = image_tag avatar_icon(user, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(user, 24), class: "avatar s24", alt: ''
       %strong
         = link_to user.name, user_path(user)
       %span.cgray= user.username
@@ -14,7 +14,7 @@
         %label.label.label-danger
           %strong Blocked
     - else
-      = image_tag avatar_icon(member.invite_email, 16), class: "avatar s16", alt: ''
+      = image_tag avatar_icon(member.invite_email, 24), class: "avatar s24", alt: ''
       %strong
         = member.invite_email
       %span.cgray
@@ -24,18 +24,19 @@
           = link_to member.created_by.name, user_path(member.created_by)
         = time_ago_with_tooltip(member.created_at)
 
-      - if current_user_can_admin_project
+      - if can?(current_user, :admin_project_member, @project)
         = link_to resend_invite_namespace_project_project_member_path(@project.namespace, @project, member), method: :post, class: "btn-xs btn", title: 'Resend invite' do
           Resend invite
 
-  - if current_user_can_admin_project
-    - unless @project.personal? && user == current_user
-      .pull-right
-        %strong= member.human_access
+  - if can?(current_user, :admin_project_member, @project)
+    .pull-right
+      %strong= member.human_access
+      - if can?(current_user, :update_project_member, member)
         = button_tag class: "btn-xs btn js-toggle-button",
                      title: 'Edit access level', type: 'button' do
           %i.fa.fa-pencil-square-o
 
+      - if can?(current_user, :destroy_project_member, member)
         &nbsp;
         - if current_user == user
           = link_to leave_namespace_project_project_members_path(@project.namespace, @project), data: { confirm: leave_project_message(@project) }, method: :delete, class: "btn-xs btn btn-remove", title: 'Leave project' do
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 615c425e59ae2967a6ba90f5d7ff6ad8bd83103d..ccddab13aafb82e5f6d932be48d9f6525158a8fa 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -1,11 +1,21 @@
-- can_admin_project = can?(current_user, :admin_project, @project)
-
-.panel.panel-default.prepend-top-20
+.panel.panel-default
   .panel-heading
     %strong #{@project.name}
     project members
     %small
       (#{members.count})
-  %ul.well-list
+    .pull-right
+      = 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', spellcheck: false }
+        = button_tag class: 'btn', title: 'Search' do
+          = icon("search")
+  %ul.content-list
     - members.each do |project_member|
-      = render 'project_member', member: project_member, current_user_can_admin_project: can_admin_project
+      = render 'project_member', member: project_member
+
+:javascript
+  $('form.member-search-form').on('submit', function (event) {
+    event.preventDefault();
+    Turbolinks.visit(this.action + '?' + $(this).serialize());
+  });
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 9fc4be583cc3fa6740a9a66a8d9579ee5f96826b..29225a3636455d35c8727c51ab0ef86e0eb6660a 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -1,36 +1,21 @@
 - page_title "Members"
 = render "header_title"
+- @blank_container = true
 
-.gray-content-block.top-block
-  .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', spellcheck: false }
-      = button_tag 'Search', class: 'btn'
-
-    - if can?(current_user, :admin_project_member, @project)
-      %span.pull-right
-        = button_tag class: 'btn btn-new btn-grouped js-toggle-button', type: 'button' do
-          Add members
-          %i.fa.fa-chevron-down
-        = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
-          Import members
-
-      .js-toggle-content.hide.new-group-member-holder
+.project-members-page
+  - if can?(current_user, :admin_project_member, @project)
+    .panel.panel-default
+      .panel-heading
+        Add new user to project
+        .pull-right
+          = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do
+            Import members
+      .panel-body
+        %p.light
+          Users with access to this project are listed below.
         = render "new_project_member"
 
-%p.prepend-top-default.light
-  Users with access to this project are listed below.
-  Read more about project permissions
-  %strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink"
-
-= render "team", members: @project_members
-
-- if @group
-  = render "group_members", members: @group_members
+  = render "team", members: @project_members
 
-:javascript
-  $('form.member-search-form').on('submit', function (event) {
-    event.preventDefault();
-    Turbolinks.visit(this.action + '?' + $(this).serialize());
-  });
+  - if @group
+    = render "group_members", members: @group_members
diff --git a/app/views/projects/project_members/update.js.haml b/app/views/projects/project_members/update.js.haml
index 811b1858821c93518116a449322dc0b161954f77..2fb3a41d541051ba1fa06df53f611abb0e60b4c5 100644
--- a/app/views/projects/project_members/update.js.haml
+++ b/app/views/projects/project_members/update.js.haml
@@ -1,3 +1,2 @@
-- can_admin_project = can?(current_user, :admin_project, @project)
 :plain
-  $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member, current_user_can_admin_project: can_admin_project))}');
+  $("##{dom_id(@project_member)}").replaceWith('#{escape_javascript(render("project_member", member: @project_member))}');
diff --git a/app/views/projects/releases/edit.html.haml b/app/views/projects/releases/edit.html.haml
index e7db09cdaa9153b3ec8957ce0950fb8993b9b4c5..bc80f2f29adb4491574f3b452a32f70e841c6d36 100644
--- a/app/views/projects/releases/edit.html.haml
+++ b/app/views/projects/releases/edit.html.haml
@@ -11,10 +11,9 @@
 .prepend-top-default
   = form_for(@release, method: :put, url: namespace_project_tag_release_path(@project.namespace, @project, @tag.name), html: { class: 'form-horizontal gfm-form release-form' }) do |f|
     = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
-      = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit'
+      = render 'projects/zen', f: f, attr: :description, classes: 'description js-quick-submit form-control'
       = render 'projects/notes/hints'
       .error-alert
-      .prepend-top-default
+      .form-actions.prepend-top-default
         = f.submit 'Save changes', class: 'btn btn-save'
         = link_to "Cancel", namespace_project_tag_path(@project.namespace, @project, @tag.name), class: "btn btn-default btn-cancel"
-
diff --git a/app/views/projects/repositories/_download_archive.html.haml b/app/views/projects/repositories/_download_archive.html.haml
index 07c24950ee238e89c7d5744d79ea54bd79d5db6d..b9486a9b49265c92771b58c529b26fd9f51cb4de 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 btn-success col-xs-10', rel: 'nofollow' do
+    = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: ref, format: 'zip'), class: 'btn col-xs-10', rel: 'nofollow' do
       %i.fa.fa-download
       %span Download zip
-    %a.col-xs-2.btn.btn-success.dropdown-toggle{ 'data-toggle' => 'dropdown' }
+    %a.col-xs-2.btn.dropdown-toggle{ 'data-toggle' => 'dropdown' }
       %span.caret
       %span.sr-only
         Select Archive Format
diff --git a/app/views/projects/runners/edit.html.haml b/app/views/projects/runners/edit.html.haml
index dde9e448cb92194e2c7efd70c4914712c3c59d21..a032470169060698db8f86ba6f4cbaf3ac84587c 100644
--- a/app/views/projects/runners/edit.html.haml
+++ b/app/views/projects/runners/edit.html.haml
@@ -23,7 +23,7 @@
     = label_tag :tag_list, class: 'control-label' do
       Tags
     .col-sm-10
-      = f.text_field :tag_list, class: 'form-control'
+      = f.text_field :tag_list, value: @runner.tag_list.to_s, class: 'form-control'
       .help-block You can setup jobs to only use runners with specific tags
   .form-actions
     = f.submit 'Save', class: 'btn btn-save'
diff --git a/app/views/projects/show.atom.builder b/app/views/projects/show.atom.builder
index 242684e5c7cba676d10a4906a0f7c2fc9d0b1856..15c49767556dbf9593da79190cbfcda4e96b6af8 100644
--- a/app/views/projects/show.atom.builder
+++ b/app/views/projects/show.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: namespace_project_url(@project.namespace, @project, format: :atom, private_token: current_user.try(:private_token)), rel: "self", type: "application/atom+xml"
   xml.link    href: namespace_project_url(@project.namespace, @project), rel: "alternate", type: "text/html"
   xml.id      namespace_project_url(@project.namespace, @project)
-  xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+  xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
     event_to_atom(xml, event)
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 585caf674c961982e849ec5fad5dd7bf4e27529b..9c7a5584da9a1122a09121c940dc26c29da46461 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -11,7 +11,7 @@
 
 = render "home_panel"
 
-.project-stats.gray-content-block
+.project-stats.gray-content-block.second-block
   %ul.nav.nav-pills
     %li
       = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
diff --git a/app/views/projects/tags/index.html.haml b/app/views/projects/tags/index.html.haml
index 85d76eae3b518e34b16c9777c98485e0911cc40b..760347de0a9943a853c4e23770b3a3e305580779 100644
--- a/app/views/projects/tags/index.html.haml
+++ b/app/views/projects/tags/index.html.haml
@@ -6,7 +6,7 @@
   - if can? current_user, :push_code, @project
     .pull-right
       = link_to new_namespace_project_tag_path(@project.namespace, @project), class: 'btn btn-create new-tag-btn' do
-        %i.fa.fa-add-sign
+        = icon('plus')
         New tag
   .oneline
     Tags give the ability to mark specific points in history as being important
diff --git a/app/views/projects/tags/new.html.haml b/app/views/projects/tags/new.html.haml
index e106be794f1a5fd31c0e79c59d69e7afe8f0fa8c..86aa15dc5b3e209bbcd2a8243f1cc140a493a3f6 100644
--- a/app/views/projects/tags/new.html.haml
+++ b/app/views/projects/tags/new.html.haml
@@ -10,7 +10,7 @@
   New git tag
 %hr
 
-= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal tag-form" do
+= form_tag namespace_project_tags_path, method: :post, id: "new-tag-form", class: "form-horizontal gfm-form tag-form" do
   .form-group
     = label_tag :tag_name, 'Name for new tag', class: 'control-label'
     .col-sm-10
@@ -30,16 +30,7 @@
     = label_tag :release_description, 'Release notes', class: 'control-label'
     .col-sm-10
       = render layout: 'projects/md_preview', locals: { preview_class: "md-preview", referenced_users: true } do
-        .zennable
-          %input#zen-toggle-comment.zen-toggle-comment(tabindex="-1" type="checkbox")
-          .zen-backdrop
-            = text_area_tag :release_description, nil, class: 'js-gfm-input markdown-area description js-quick-submit form-control', placeholder: ''
-            %a.zen-enter-link(tabindex="-1" href="#")
-              = icon('expand')
-              Edit in fullscreen
-            %a.zen-leave-link(href="#")
-              = icon('compress')
-
+        = render 'projects/zen', attr: :release_description, classes: 'description js-quick-submit form-control'
         = render 'projects/notes/hints'
         .help-block (Optional) You can add release notes to your tag. It will be stored in the GitLab database and shown on the tags page
   .form-actions
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index ebe3718afcc3bbf8cb55f80e5d4ce016c94fd09d..879c6c7d310bccd5ebd76d3a744d0c6c0b09dfe2 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -15,7 +15,7 @@
       = render 'projects/tags/download', ref: @tag.name, project: @project
     - if can?(current_user, :admin_project, @project)
       .pull-right
-        = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: 'Removed tag cannot be restored. Are you sure?'} do
+        = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped', method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do
           %i.fa.fa-trash-o
   .title
     %strong= @tag.name
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index ee4c9d1693d1c7ded187ef654ea979e090e7fe55..1bc90edd8f0342c35352cd162328c55a14e3f00f 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -12,7 +12,7 @@
               %i.fa.fa-angle-right
               &nbsp;
               %small.light
-                = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit)
+                = link_to @commit.short_id, namespace_project_commit_path(@project.namespace, @project, @commit), class: "monospace"
                 &ndash;
                 = truncate(@commit.title, length: 50)
             = link_to 'History', namespace_project_commits_path(@project.namespace, @project, @id), class: 'pull-right'
@@ -30,7 +30,7 @@
     = render "projects/tree/readme", readme: tree.readme
 
 - if allowed_tree_edit?
-  = render 'projects/blob/upload', title: 'Upload', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
+  = render 'projects/blob/upload', title: 'Upload New File', placeholder: 'Upload new file', button_title: 'Upload file', form_path: namespace_project_create_blob_path(@project.namespace, @project, @id), method: :post
   = render 'projects/blob/new_dir'
 
 :javascript
diff --git a/app/views/projects/wikis/_form.html.haml b/app/views/projects/wikis/_form.html.haml
index 9c94c43e747e776d135344a45dbc1746803ee42b..1d257818dcdbe03fb15a1524b808ec65f83a0431 100644
--- a/app/views/projects/wikis/_form.html.haml
+++ b/app/views/projects/wikis/_form.html.haml
@@ -1,4 +1,4 @@
-= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form' } do |f|
+= form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post, html: { class: 'form-horizontal wiki-form gfm-form prepend-top-default' } do |f|
   -if @page.errors.any?
     #error_explanation
       .alert.alert-danger
@@ -11,14 +11,7 @@
     .col-sm-10
       = f.select :format, options_for_select(ProjectWiki::MARKUPS, {selected: @page.format}), {}, class: "form-control"
 
-  .row
-    .col-sm-offset-2.col-sm-10
-      %p.cgray
-        To link to a (new) page you can just type
-        %code [Link Title](page-slug)
-        \.
-
-  .form-group.wiki-content
+  .form-group
     = f.label :content, class: 'control-label'
     .col-sm-10
       = render layout: 'projects/md_preview', locals: { preview_class: "md-preview" } do
@@ -27,6 +20,11 @@
 
       .clearfix
       .error-alert
+
+      .help-block
+        To link to a (new) page, simply type
+        %code [Link Title](page-slug)
+        \.
   .form-group
     = f.label :commit_message, class: 'control-label'
     .col-sm-10= f.text_field :message, class: 'form-control', rows: 18
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 14f25822259ccea685036107f600b24b5749b0bb..29bf5d62abed4605ede417c6839eba190f98c0e2 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,9 +1,4 @@
 %span.pull-right
-  - if can?(current_user, :create_wiki, @project)
-    = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new btn-grouped", "data-toggle" => "modal" do
-      %i.fa.fa-plus
-      New Page
-
   - if (@page && @page.persisted?)
     = link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
       Page History
@@ -11,5 +6,7 @@
       = link_to namespace_project_wiki_edit_path(@project.namespace, @project, @page), class: "btn btn-grouped" do
         %i.fa.fa-pencil-square-o
         Edit
-
-= render 'projects/wikis/new'
+    - if can?(current_user, :admin_wiki, @project)
+      = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-remove" do
+        = icon('trash')
+        Delete
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index fffb4eb31aba3c4be3714632364c4c7ba69d9ee2..e6e6ad5bc4b631473f8970d19b05b2cd70f8d85c 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,10 +1,19 @@
-%ul.center-top-menu
-  = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
-    = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
+.project-issuable-filter
+  .controls
+    - if can?(current_user, :create_wiki, @project)
+      = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+        %i.fa.fa-plus
+        New Page
 
-  = nav_link(path: 'wikis#pages') do
-    = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
+      = render 'projects/wikis/new'
 
-  = nav_link(path: 'wikis#git_access') do
-    = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
-      Git Access
+  %ul.center-top-menu
+    = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
+      = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
+
+    = nav_link(path: 'wikis#pages') do
+      = link_to 'Pages', namespace_project_wiki_pages_path(@project.namespace, @project)
+
+    = nav_link(path: 'wikis#git_access') do
+      = link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
+        Git Access
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index dace172438cd6268ccef2a6c13fca80e6f3b28d1..f0547e9c0575d7259e853b44949b440d58d84604 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -12,5 +12,5 @@
           The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and /
         %p.hint
           Please don't use spaces.
-      .modal-footer
-        = link_to 'Build', '#', class: 'build-new-wiki btn btn-create'
+        .form-actions
+          = link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index 0b709c3695b8f908584bc1d1837a4a21a33d35da..23f64fbbd1062d33ccac23854bd874dcf4072d2e 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,16 +1,16 @@
-- page_title "Edit", @page.title, "Wiki"
+- page_title "Edit", @page.title.capitalize, "Wiki"
 = render "header_title"
 
 = render 'nav'
-.pull-right
-  = render 'main_links'
-%h3.page-title
-  Editing -
-  %span.light #{@page.title}
-%hr
-= render 'form'
+.gray-content-block
+  .pull-right
+    = render 'main_links'
+
+  %h3.page-title.oneline
+    %span.light Edit Page
+    - if @page.persisted?
+      = link_to @page.title, namespace_project_wiki_path(@project.namespace, @project, @page)
+    - else
+      = @page.title
 
-.pull-right
-  - if @page.persisted? && can?(current_user, :admin_wiki, @project)
-    = link_to namespace_project_wiki_path(@project.namespace, @project, @page), data: { confirm: "Are you sure you want to delete this page?"}, method: :delete, class: "btn btn-sm btn-remove" do
-      Delete this page
+= render 'form'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 6417ef4a38bae363a077aae039b70e2d3f67a8e7..11c8c4f0eba81ae1273a88880999d332c314719b 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -5,7 +5,7 @@
 .gray-content-block
   .row
     .col-sm-6
-      %h3.page-title
+      %h3.page-title.oneline
         Git access for
         %strong= @project_wiki.path_with_namespace
 
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index d179a1abec1323380c4de50cc3cdb40f7a1f46da..aae1ad69ad90c3272cbd98418129c3d0707b11db 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,11 +1,10 @@
-- page_title "All Pages", "Wiki"
+- page_title "Pages", "Wiki"
 = render "header_title"
 
 = render 'nav'
 .gray-content-block
-  = render 'main_links'
-  %h3.page-title
-    All Pages
+  All pages in this wiki are listed below.
+  
 %ul.content-list
   - @wiki_pages.each do |wiki_page|
     %li
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 55fbf5a8b6e27f995c79652b4a5c1d98f8190549..309d40f52bc6832ddb9919a9faf591eb7da02bbf 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -5,11 +5,12 @@
 
 .gray-content-block
   = render 'main_links'
-  %h3.page-title
+  %h3.page-title.oneline
     = @page.title.capitalize
 
-  .wiki-last-edit-by
-    Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+    %span.wiki-last-edit-by
+      &middot;
+      last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
 
 - if @page.historical?
   .warning_message
@@ -21,8 +22,3 @@
   .wiki
     = preserve do
       = render_wiki_content(@page)
-
-.gray-content-block.footer-block
-  .wiki-last-edit-by
-    Last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
-
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 2e4aab36301833c1fa63becd42024df25f5dd7f1..edb5778f4240740ef66bbbf4560a3b0074b6e3b7 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -2,26 +2,9 @@
 .git-clone-holder.input-group
   .input-group-addon.git-protocols
     .input-group-btn
-      %button{ |
-        type: 'button', |
-        class: "btn #{ 'active' if default_clone_protocol == 'ssh' }#{ ' has_tooltip' if current_user && current_user.require_ssh_key? }", |
-        :"data-clone" => project.ssh_url_to_repo, |
-        :"data-title" => "Add an SSH key to your profile<br> to pull or push via SSH.",
-        :"data-html" => "true",
-        :"data-container" => "body"}
-        SSH
+      = ssh_clone_button(project)
     .input-group-btn
-      %button{ |
-        type: 'button', |
-        class: "btn #{ 'active' if default_clone_protocol == 'http' }#{ ' has_tooltip' if current_user && current_user.require_password? }", |
-        :"data-clone" => project.http_url_to_repo, |
-        :"data-title" => "Set a password on your account<br> to pull or push via #{gitlab_config.protocol.upcase}.",
-        :"data-html" => "true",
-        :"data-container" => "body"}
-        = gitlab_config.protocol.upcase
+      = http_clone_button(project)
   = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true
-  - if project.kind_of?(Project)
-    .input-group-addon
-      .visibility-level-label.has_tooltip{'data-title' => "#{visibility_level_label(project.visibility_level)} project" }
-        = visibility_level_icon(project.visibility_level)
-   
+  .input-group-btn
+    = clipboard_button(clipboard_target: '#project_clone')
diff --git a/app/views/shared/_commit_message_container.html.haml b/app/views/shared/_commit_message_container.html.haml
index cc3f1268f8b9fb8d01dcc87ecc3dcee30c51646b..7c57924277e853f82b86408eb8698ad60b336e3d 100644
--- a/app/views/shared/_commit_message_container.html.haml
+++ b/app/views/shared/_commit_message_container.html.haml
@@ -1,13 +1,15 @@
 .form-group.commit_message-group
-  = label_tag 'commit_message', class: 'control-label' do
+  - nonce = SecureRandom.hex
+  = label_tag "commit_message-#{nonce}", class: 'control-label' do
     Commit message
   .col-sm-10
     .commit-message-container
       .max-width-marker
       = text_area_tag 'commit_message',
           (params[:commit_message] || local_assigns[:text]),
-          class: 'form-control js-quick-submit', placeholder: local_assigns[:placeholder],
-          required: true, rows: (local_assigns[:rows] || 3)
+          class: 'form-control js-commit-message js-quick-submit', placeholder: local_assigns[:placeholder],
+          required: true, rows: (local_assigns[:rows] || 3),
+          id: "commit_message-#{nonce}"
     - if local_assigns[:hint]
       %p.hint
         Try to keep the first line under 52 characters
diff --git a/app/views/shared/_confirm_modal.html.haml b/app/views/shared/_confirm_modal.html.haml
index 5f51b0d450fddccd0a602d94a4e0c183a6a37cd5..2a44817e05a8a668e068c032949af3326eb4995a 100644
--- a/app/views/shared/_confirm_modal.html.haml
+++ b/app/views/shared/_confirm_modal.html.haml
@@ -14,7 +14,7 @@
           %br
           Please type
           %code.js-confirm-danger-match #{phrase}
-          to proceed or close this modal to cancel
+          to proceed or close this modal to cancel.
 
         .form-group
           = text_field_tag 'confirm_name_input', '', class: 'form-control js-confirm-danger-input'
diff --git a/app/views/shared/_import_form.html.haml b/app/views/shared/_import_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..285af56ad73c28bcf4a30a80c974669377138df7
--- /dev/null
+++ b/app/views/shared/_import_form.html.haml
@@ -0,0 +1,16 @@
+.form-group.import-url-data
+  = f.label :import_url, class: 'control-label' do
+    %span Git repository URL
+  .col-sm-10
+    = f.text_field :import_url, class: 'form-control', placeholder: 'https://username:password@gitlab.company.com/group/project.git'
+
+    .well.prepend-top-20
+      %ul
+        %li
+          The repository must be accessible over <code>http://</code>, <code>https://</code> or <code>git://</code>.
+        %li
+          If your HTTP repository is not publicly accessible, add authentication information to the URL: <code>https://username:password@gitlab.company.com/group/project.git</code>.
+        %li
+          The import will time out after 4 minutes. For big repositories, use a clone/push combination.
+        %li
+          To migrate an SVN repository, check out #{link_to "this document", "http://doc.gitlab.com/ce/workflow/importing/migrating_from_svn.html"}.
diff --git a/app/views/shared/_issues.html.haml b/app/views/shared/_issues.html.haml
index 0dbb6a04393720ada519a3b81d158f1a6e7f6b27..4b4c9e9eabe20248a893f855cbada00e0bb11327 100644
--- a/app/views/shared/_issues.html.haml
+++ b/app/views/shared/_issues.html.haml
@@ -3,8 +3,10 @@
     .panel.panel-default.panel-small
       - project = group[0]
       .panel-heading
-        = link_to_project project
-        = link_to 'show all', namespace_project_issues_path(project.namespace, project), class: 'pull-right'
+        = link_to project.name_with_namespace, namespace_project_issues_path(project.namespace, project)
+        - if can?(current_user, :create_issue, project)
+          .pull-right
+            = link_to 'New issue', new_namespace_project_issue_path(project.namespace, project)
 
       %ul.well-list.issues-list
         - group[1].each do |issue|
@@ -12,4 +14,3 @@
   = paginate @issues, theme: "gitlab"
 - else
   .nothing-here-block No issues to show
-
diff --git a/app/views/shared/_merge_requests.html.haml b/app/views/shared/_merge_requests.html.haml
index c02c5af008a35deffb3599f5c40ac886827e6744..be17a511b26c7ec0fef762d433d8667115b99d16 100644
--- a/app/views/shared/_merge_requests.html.haml
+++ b/app/views/shared/_merge_requests.html.haml
@@ -3,8 +3,11 @@
     .panel.panel-default.panel-small
       - project = group[0]
       .panel-heading
-        = link_to_project project
-        = link_to 'show all', namespace_project_merge_requests_path(project.namespace, project), class: 'pull-right'
+        = link_to project.name_with_namespace, namespace_project_merge_requests_path(project.namespace, project)
+        - if can?(current_user, :create_merge_request, project)
+          .pull-right
+            = link_to 'New merge request', new_namespace_project_merge_request_path(project.namespace, project)
+
       %ul.well-list.mr-list
         - group[1].each do |merge_request|
           = render 'projects/merge_requests/merge_request', merge_request: merge_request
diff --git a/app/views/shared/_new_commit_form.html.haml b/app/views/shared/_new_commit_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..8636341c60dbe99f177c5d0be368bb40de421ede
--- /dev/null
+++ b/app/views/shared/_new_commit_form.html.haml
@@ -0,0 +1,18 @@
+= render 'shared/commit_message_container', placeholder: placeholder
+
+- unless @project.empty_repo?
+  .form-group.branch
+    = label_tag 'branch', class: 'control-label' do
+      Branch
+    .col-sm-10
+      = text_field_tag 'new_branch', @new_branch || @ref, class: "form-control js-new-branch"
+
+  .form-group.js-create-merge-request-form-group
+    .col-sm-offset-2.col-sm-10
+      .checkbox
+        - nonce = SecureRandom.hex
+        = label_tag "create_merge_request-#{nonce}" do
+          = check_box_tag 'create_merge_request', 1, true, class: 'js-create-merge-request', id: "create_merge_request-#{nonce}"
+          Start a <strong>new merge request</strong> with this commit
+
+  = hidden_field_tag 'original_branch', @ref, class: 'js-original-branch'
diff --git a/app/views/shared/issuable/_context.html.haml b/app/views/shared/issuable/_context.html.haml
index be66256c7b052d3ed05273afab50fa6faadd7922..f1646b4ce64f03f00372e8d7de53aaf29a01df34 100644
--- a/app/views/shared/issuable/_context.html.haml
+++ b/app/views/shared/issuable/_context.html.haml
@@ -1,5 +1,5 @@
 = form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
-  %div.prepend-top-20
+  %div.prepend-top-default
     .issuable-context-title
       %label
         Assignee:
@@ -11,7 +11,7 @@
       - if can?(current_user, :"admin_#{issuable.to_ability_name}", @project)
         = users_select_tag("#{issuable.class.table_name.singularize}[assignee_id]", placeholder: 'Select assignee', class: 'custom-form-control js-select2 js-assignee', selected: issuable.assignee_id, project: @target_project, null_user: true, current_user: true)
 
-  %div.prepend-top-20.clearfix
+  %div.prepend-top-default.clearfix
     .issuable-context-title
       %label
         Milestone:
@@ -31,7 +31,7 @@
 
   - if current_user
     - subscribed = issuable.subscribed?(current_user)
-    %div.prepend-top-20.clearfix
+    %div.prepend-top-default.clearfix
       .issuable-context-title
         %label
           Subscription:
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index d1231438ee4df03c2f04743dbce48957cdb737be..ac6c248ccf1df9f6e6b67dff7ce1ecb157f60b16 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -31,11 +31,11 @@
       .issues-other-filters
         .filter-item.inline
           = users_select_tag(:assignee_id, selected: params[:assignee_id],
-            placeholder: 'Assignee', class: 'trigger-submit', any_user: true, null_user: true, first_user: true, current_user: true)
+            placeholder: 'Assignee', class: 'trigger-submit', any_user: "Any Assignee", null_user: true, first_user: true, current_user: true)
 
         .filter-item.inline
           = users_select_tag(:author_id, selected: params[:author_id],
-            placeholder: 'Author', class: 'trigger-submit', any_user: true, first_user: true, current_user: true)
+            placeholder: 'Author', class: 'trigger-submit', any_user: "Any Author", first_user: true, current_user: true)
 
         .filter-item.inline.milestone-filter
           = select_tag('milestone_title', projects_milestones_options,
@@ -53,12 +53,16 @@
     - if controller.controller_name == 'issues'
       .issues_bulk_update.hide
         = form_tag bulk_update_namespace_project_issues_path(@project.namespace, @project), method: :post  do
-          = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), prompt: "Status", class: 'form-control')
-          = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true)
-          = select_tag('update[milestone_id]', bulk_update_milestone_options, prompt: "Milestone")
+          .filter-item.inline
+            = select_tag('update[state_event]', options_for_select([['Open', 'reopen'], ['Closed', 'close']]), include_blank: true, data: { placeholder: "Status" })
+          .filter-item.inline
+            = users_select_tag('update[assignee_id]', placeholder: 'Assignee', null_user: true, first_user: true, current_user: true)
+          .filter-item.inline
+            = select_tag('update[milestone_id]', bulk_update_milestone_options, include_blank: true, data: { placeholder: "Milestone" })
           = hidden_field_tag 'update[issues_ids]', []
           = hidden_field_tag :state_event, params[:state_event]
-          = button_tag "Update issues", class: "btn update_selected_issues btn-save"
+          .filter-item.inline
+            = button_tag "Update issues", class: "btn update_selected_issues btn-save"
 
 :javascript
   new UsersSelect();
diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml
index 0fc74d7d2b162825a1e086d54da83811bca0390e..7558b37f83fd53218186c4f1521d7227a5ec0dfc 100644
--- a/app/views/shared/issuable/_form.html.haml
+++ b/app/views/shared/issuable/_form.html.haml
@@ -93,7 +93,8 @@
         %p.help-block
         = link_to 'Change branches', mr_change_branches_path(@merge_request)
 
-.form-actions
+- is_footer = !(issuable.is_a?(MergeRequest) && issuable.new_record?)
+.gray-content-block{class: (is_footer ? "footer-block" : "middle-block")}
   - if !issuable.project.empty_repo? && (guide_url = contribution_guide_path(issuable.project)) && !issuable.persisted?
     %p
       Please review the
diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml
index 0a4a790ec5ee182b5ad0e5a62178dd0636246e45..89c1d7122b0bb582cea05a27210e91a879c9cb8a 100644
--- a/app/views/shared/snippets/_header.html.haml
+++ b/app/views/shared/snippets/_header.html.haml
@@ -1,9 +1,9 @@
-.snippet-details
+.issuable-details
   .page-title
-    .snippet-box{class: visibility_level_color(@snippet.visibility_level)}
-      = visibility_level_icon(@snippet.visibility_level)
+    .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level), data: { container: 'body' }}
+      = visibility_level_icon(@snippet.visibility_level, fw: false)
       = visibility_level_label(@snippet.visibility_level)
-    %span.snippet-id Snippet ##{@snippet.id}
+    Snippet ##{@snippet.id}
     %span.creator
       &middot; created by #{link_to_member(@project, @snippet.author, size: 24)}
       &middot;
@@ -19,6 +19,7 @@
         = render "projects/snippets/actions"
       - else
         = render "snippets/actions"
+
   .gray-content-block.middle-block
-    %h2.snippet-title
+    %h2.issue-title
       = gfm escape_once(@snippet.title)
diff --git a/app/views/sherlock/transactions/_general.html.haml b/app/views/sherlock/transactions/_general.html.haml
index 4287a0c32036f49cccd1b7ac1482186d7f1f5254..8533b130da684d588917991f92e67acd6715d087 100644
--- a/app/views/sherlock/transactions/_general.html.haml
+++ b/app/views/sherlock/transactions/_general.html.haml
@@ -25,6 +25,12 @@
         %strong
           = @transaction.duration.round(2)
           = t('sherlock.seconds')
+      %li
+        %span.light
+          #{t('sherlock.query_time')}
+        %strong
+          = @transaction.query_duration.round(2)
+          = t('sherlock.seconds')
       %li
         %span.light
           #{t('sherlock.finished_at')}:
diff --git a/app/views/users/show.atom.builder b/app/views/users/show.atom.builder
index 50232dc7186054179c5a5477948e86507079819c..2fe5b7fac8327ff384fd28bb8cc39b405269bf09 100644
--- a/app/views/users/show.atom.builder
+++ b/app/views/users/show.atom.builder
@@ -4,7 +4,7 @@ xml.feed "xmlns" => "http://www.w3.org/2005/Atom", "xmlns:media" => "http://sear
   xml.link    href: user_url(@user, :atom), rel: "self", type: "application/atom+xml"
   xml.link    href: user_url(@user), rel: "alternate", type: "text/html"
   xml.id      user_url(@user)
-  xml.updated @events.maximum(:updated_at).strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
+  xml.updated @events.latest_update_time.strftime("%Y-%m-%dT%H:%M:%SZ") if @events.any?
 
   @events.each do |event|
     event_to_atom(xml, event)
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 30992412184e6a4cea4b00d86122c3912fd5cfd2..d5a92cb816adf83ea7e507a43a91ca97b41c855d 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -32,11 +32,11 @@
           = icon('skype')
     - unless @user.linkedin.blank?
       .profile-link-holder
-        = link_to "http://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
+        = link_to "https://www.linkedin.com/in/#{@user.linkedin}", title: "LinkedIn" do
           = icon('linkedin-square')
     - unless @user.twitter.blank?
       .profile-link-holder
-        = link_to "http://www.twitter.com/#{@user.twitter}", title: "Twitter" do
+        = link_to "https://twitter.com/#{@user.twitter}", title: "Twitter" do
           = icon('twitter-square')
     - unless @user.website_url.blank?
       .profile-link-holder
diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml
index 36ea67420641ee8af1d63bf450107dc4e56f00ba..7eb27c12d3395028474f9cb969367e500be3f85b 100644
--- a/app/views/votes/_votes_block.html.haml
+++ b/app/views/votes/_votes_block.html.haml
@@ -1,10 +1,32 @@
-.votes.votes-block
-  .btn-group
-    - unless votable.upvotes.zero?
-      .btn.btn-sm.disabled.cgreen
-        %i.fa.fa-thumbs-up
-        = votable.upvotes
-    - unless votable.downvotes.zero?
-      .btn.btn-sm.disabled.cred
-        %i.fa.fa-thumbs-down
-        = votable.downvotes
+.awards.votes-block
+  - votable.notes.awards.grouped_awards.each do |emoji, notes|
+    .award{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user)}
+      .icon{"data-emoji" => "#{emoji}"}
+        = image_tag url_to_emoji(emoji), height: "20px", width: "20px"
+      .counter
+        = notes.count
+
+  - if current_user
+    .dropdown.awards-controls
+      %a.add-award{"data-toggle" => "dropdown", "data-target" => "#", "href" => "#"}
+        = icon('smile-o')
+      %ul.dropdown-menu.awards-menu
+        - emoji_list.each do |emoji|
+          %li{"data-emoji" => "#{emoji}"}= image_tag url_to_emoji(emoji), height: "20px", width: "20px"
+
+- if current_user
+  :coffeescript
+    post_emoji_url = "#{award_toggle_namespace_project_notes_path(@project.namespace, @project)}"
+    noteable_type = "#{votable.class.name.underscore}"
+    noteable_id = "#{votable.id}"
+    window.awards_handler = new AwardsHandler(post_emoji_url, noteable_type, noteable_id)
+
+    $(".awards-menu li").click (e)->
+      emoji = $(this).data("emoji")
+      awards_handler.addAward(emoji)
+
+    $(".awards").on "click", ".award", (e)->
+      emoji = $(this).find(".icon").data("emoji")
+      awards_handler.addAward(emoji)
+
+    $(".award").tooltip()
diff --git a/app/views/votes/_votes_inline.html.haml b/app/views/votes/_votes_inline.html.haml
deleted file mode 100644
index 2cb3ae04e1a83b670b824993ee5a59e1dcf29684..0000000000000000000000000000000000000000
--- a/app/views/votes/_votes_inline.html.haml
+++ /dev/null
@@ -1,9 +0,0 @@
-.votes.votes-inline
-  - unless votable.upvotes.zero?
-    %span.upvotes.cgreen
-      + #{votable.upvotes}
-    - unless votable.downvotes.zero?
-      \/
-  - unless votable.downvotes.zero?
-    %span.downvotes.cred
-      \- #{votable.downvotes}
diff --git a/app/workers/email_receiver_worker.rb b/app/workers/email_receiver_worker.rb
index 5a921a73fe9b46faea572994d9d7c65d9d60f6be..f2649e38eb34baafeaf2eded7f17821ff46ade8f 100644
--- a/app/workers/email_receiver_worker.rb
+++ b/app/workers/email_receiver_worker.rb
@@ -46,6 +46,6 @@ class EmailReceiverWorker
       return
     end
 
-    EmailRejectionMailer.delay.rejection(reason, raw, can_retry)
+    EmailRejectionMailer.rejection(reason, raw, can_retry).deliver_later
   end
 end
diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb
index 916a99bb273b8aadb85a6ff45541d4209d2dabc3..c4d8595d45dc9f9297c07ae75a0438aafcf59691 100644
--- a/app/workers/emails_on_push_worker.rb
+++ b/app/workers/emails_on_push_worker.rb
@@ -53,7 +53,7 @@ class EmailsOnPushWorker
           reverse_compare:            reverse_compare,
           send_from_committer_email:  send_from_committer_email,
           disable_diffs:              disable_diffs
-        ).deliver
+        ).deliver_now
       # These are input errors and won't be corrected even if Sidekiq retries
       rescue Net::SMTPFatalError, Net::SMTPSyntaxError => e
         logger.info("Failed to send e-mail for project '#{project.name_with_namespace}' to #{recipient}: #{e}")
diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb
index acd1c43f06b7d4e33277c0cca834fbf13280c20c..2f991c523390af1ab894a067b9a6f3ccd8921cd4 100644
--- a/app/workers/repository_fork_worker.rb
+++ b/app/workers/repository_fork_worker.rb
@@ -13,22 +13,20 @@ class RepositoryForkWorker
     end
 
     result = gitlab_shell.fork_repository(source_path, target_path)
-
     unless result
       logger.error("Unable to fork project #{project_id} for repository #{source_path} -> #{target_path}")
+      project.update(import_error: "The project could not be forked.")
       project.import_fail
-      project.save
       return
     end
 
-    if project.valid_repo?
-      ProjectCacheWorker.perform_async(project.id)
-      project.import_finish
-    else
-      project.import_fail
+    unless project.valid_repo?
       logger.error("Project #{id} had an invalid repository after fork")
+      project.update(import_error: "The forked repository is invalid.")
+      project.import_fail
+      return
     end
 
-    project.save
+    project.import_finish
   end
 end
diff --git a/app/workers/repository_import_worker.rb b/app/workers/repository_import_worker.rb
index ea2808045eb2e93c70202b089f1d82f1423c0338..d18c0706b303d7722585914633d43ac8d0aaed0f 100644
--- a/app/workers/repository_import_worker.rb
+++ b/app/workers/repository_import_worker.rb
@@ -7,37 +7,49 @@ class RepositoryImportWorker
   def perform(project_id)
     project = Project.find(project_id)
 
-    unless project.import_url == Project::UNKNOWN_IMPORT_URL
-      import_result = gitlab_shell.send(:import_repository,
-                               project.path_with_namespace,
-                               project.import_url)
-      return project.import_fail unless import_result
-    else
+    if project.import_url == Project::UNKNOWN_IMPORT_URL
+      # In this case, we only want to import issues, not a repository.
       unless project.create_repository
-        return project.import_fail
+        project.update(import_error: "The repository could not be created.")
+        project.import_fail
+        return
+      end
+    else
+      begin
+        gitlab_shell.import_repository(project.path_with_namespace, project.import_url)
+      rescue Gitlab::Shell::Error => e
+        project.update(import_error: e.message)
+        project.import_fail
+        return
       end
     end
 
-    data_import_result = case project.import_type
-                         when 'github'
-                           Gitlab::GithubImport::Importer.new(project).execute
-                         when 'gitlab'
-                           Gitlab::GitlabImport::Importer.new(project).execute
-                         when 'bitbucket'
-                           Gitlab::BitbucketImport::Importer.new(project).execute
-                         when 'google_code'
-                           Gitlab::GoogleCodeImport::Importer.new(project).execute
-                         when 'fogbugz'
-                           Gitlab::FogbugzImport::Importer.new(project).execute
-                         else
-                           true
-                         end
-    return project.import_fail unless data_import_result
+    data_import_result =
+      case project.import_type
+      when 'github'
+        Gitlab::GithubImport::Importer.new(project).execute
+      when 'gitlab'
+        Gitlab::GitlabImport::Importer.new(project).execute
+      when 'bitbucket'
+        Gitlab::BitbucketImport::Importer.new(project).execute
+      when 'google_code'
+        Gitlab::GoogleCodeImport::Importer.new(project).execute
+      when 'fogbugz'
+        Gitlab::FogbugzImport::Importer.new(project).execute
+      else
+        true
+      end
 
-    Gitlab::BitbucketImport::KeyDeleter.new(project).execute if project.import_type == 'bitbucket'
+    unless data_import_result
+      project.update(import_error: "The remote issue data could not be imported.")
+      project.import_fail
+      return
+    end
+
+    if project.import_type == 'bitbucket'
+      Gitlab::BitbucketImport::KeyDeleter.new(project).execute
+    end
 
     project.import_finish
-    project.save
-    ProjectCacheWorker.perform_async(project.id)
   end
 end
diff --git a/app/workers/stuck_ci_builds_worker.rb b/app/workers/stuck_ci_builds_worker.rb
index ad02a3b16d953b46eb5901e2be171a2211aa4382..4e5eddbaba1c68a8de1717eac0dab0f075544c56 100644
--- a/app/workers/stuck_ci_builds_worker.rb
+++ b/app/workers/stuck_ci_builds_worker.rb
@@ -14,5 +14,8 @@ class StuckCiBuildsWorker
       Rails.logger.debug "Dropping stuck #{build.status} build #{build.id} for runner #{build.runner_id}"
       build.drop
     end
+
+    # Update builds that failed to drop
+    builds.update_all(status: 'failed')
   end
 end
diff --git a/bin/ci/upgrade.rb b/bin/ci/upgrade.rb
old mode 100644
new mode 100755
diff --git a/bin/rails b/bin/rails
index 7feb6a30e696bf9b94f204c6abb5c4d75f965046..5191e6927af7238928ada521f47e6c3457371baf 100755
--- a/bin/rails
+++ b/bin/rails
@@ -1,8 +1,4 @@
 #!/usr/bin/env ruby
-begin
-  load File.expand_path("../spring", __FILE__)
-rescue LoadError
-end
-APP_PATH = File.expand_path('../../config/application',  __FILE__)
+APP_PATH = File.expand_path('../../config/application', __FILE__)
 require_relative '../config/boot'
 require 'rails/commands'
diff --git a/bin/rake b/bin/rake
index 0fb4e07e13ab9fdf8a7a2a86d0f7426432230bcf..17240489f64832c9ce080088e27780d3dc3ee29a 100755
--- a/bin/rake
+++ b/bin/rake
@@ -1,7 +1,4 @@
 #!/usr/bin/env ruby
-begin
-  load File.expand_path("../spring", __FILE__)
-rescue LoadError
-end
-require 'bundler/setup'
-load Gem.bin_path('rake', 'rake')
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 0000000000000000000000000000000000000000..acdb2c1389c502f79a967384cac1c5adcea10ec2
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,29 @@
+#!/usr/bin/env ruby
+require 'pathname'
+
+# path to your application root.
+APP_ROOT = Pathname.new File.expand_path('../../',  __FILE__)
+
+Dir.chdir APP_ROOT do
+  # This script is a starting point to setup your application.
+  # Add necessary setup steps to this file:
+
+  puts "== Installing dependencies =="
+  system "gem install bundler --conservative"
+  system "bundle check || bundle install"
+
+  # puts "\n== Copying sample files =="
+  # unless File.exist?("config/database.yml")
+  #   system "cp config/database.yml.sample config/database.yml"
+  # end
+
+  puts "\n== Preparing database =="
+  system "bin/rake db:setup"
+
+  puts "\n== Removing old logs and tempfiles =="
+  system "rm -f log/*"
+  system "rm -rf tmp/cache"
+
+  puts "\n== Restarting application server =="
+  system "touch tmp/restart.txt"
+end
diff --git a/bin/upgrade.rb b/bin/upgrade.rb
old mode 100644
new mode 100755
diff --git a/config/application.rb b/config/application.rb
index bfa2a809dd7d7fd472990b00d6f38c8948fd36ce..d255ff0719f52405d2d0d9eac1868d6bb4027d03 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -99,6 +99,10 @@ module Gitlab
     redis_config_hash[:expires_in] = 2.weeks # Cache should not grow forever
     config.cache_store = :redis_store, redis_config_hash
 
+    config.active_record.raise_in_transactional_callbacks = true
+
+    config.active_job.queue_adapter = :sidekiq
+
     # This is needed for gitlab-shell
     ENV['GITLAB_PATH_OUTSIDE_HOOK'] = ENV['PATH']
   end
diff --git a/config/environment.rb b/config/environment.rb
index 3b186a9d57afddfdae94306877719d2b98ea1945..df3006d349c2ce2456c6225cb0fcdf1f621c4390 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -2,4 +2,4 @@
 require File.expand_path('../application', __FILE__)
 
 # Initialize the rails application
-Gitlab::Application.initialize!
+Rails.application.initialize!
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 827a110c2492ac5349277065d2a992243760bc75..c22722c606bd8cd6f30c869a425223a71823dc30 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -1,4 +1,4 @@
-Gitlab::Application.configure do
+Rails.application.configure do
   # Settings specified here will take precedence over those in config/application.rb
 
   # In the development environment your application's code is reloaded on
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 3316ece387398bf8188c9a5fb0aee12315483f50..909526605a194da423811eba359549c1e1de5391 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -1,4 +1,4 @@
-Gitlab::Application.configure do
+Rails.application.configure do
   # Settings specified here will take precedence over those in config/application.rb
 
   # Code is not reloaded between requests
@@ -9,7 +9,7 @@ Gitlab::Application.configure do
   config.action_controller.perform_caching = true
 
   # Disable Rails's static asset server (Apache or nginx will already do this)
-  config.serve_static_assets = false
+  config.serve_static_files = false
 
   # Compress JavaScripts and CSS.
   config.assets.js_compressor = :uglifier
@@ -32,7 +32,7 @@ Gitlab::Application.configure do
   # config.force_ssl = true
 
   # See everything in the log (default is :info)
-  # config.log_level = :debug
+  config.log_level = :info
 
   # Suppress 'Rendered template ...' messages in the log
   # source: http://stackoverflow.com/a/16369363
diff --git a/config/environments/test.rb b/config/environments/test.rb
index 2d5e7addcd3ebe1b6d3dc5661e962c6790415370..f96ac6f97530378c206d2cebcc0842f29c797113 100644
--- a/config/environments/test.rb
+++ b/config/environments/test.rb
@@ -1,4 +1,4 @@
-Gitlab::Application.configure do
+Rails.application.configure do
   # Settings specified here will take precedence over those in config/application.rb
 
   # The test environment is used exclusively to run your application's
@@ -8,7 +8,7 @@ Gitlab::Application.configure do
   config.cache_classes = false
 
   # Configure static asset server for tests with Cache-Control for performance
-  config.serve_static_assets = true
+  config.serve_static_files = true
   config.static_cache_control = "public, max-age=3600"
 
   # Show full error reports and disable caching
@@ -30,4 +30,8 @@ Gitlab::Application.configure do
   config.active_support.deprecation = :stderr
 
   config.eager_load = false
+
+  config.cache_store = :null_store
+
+  config.active_job.queue_adapter = :test
 end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 20894ebcdc985ad68b402832a7fd721a5469ae2d..1da42ab38f30ba27b03b9fd54b117e67a4a58d74 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -84,6 +84,7 @@ production: &base
       merge_requests: true
       wiki: true
       snippets: false
+      builds: true
 
     ## Webhook settings
     # Number of seconds to wait for HTTP response after sending webhook HTTP POST request (default: 10)
@@ -123,6 +124,18 @@ production: &base
     # The mailbox where incoming mail will end up. Usually "inbox".
     mailbox: "inbox"
 
+  ## Build Artifacts
+  artifacts:
+    enabled: true
+    # The location where build artifacts are stored (default: shared/artifacts).
+    # path: shared/artifacts
+
+  ## Git LFS
+  lfs:
+    enabled: true
+    # The location where LFS objects are stored (default: shared/lfs-objects).
+    # storage_path: shared/lfs-objects
+
   ## Gravatar
   ## For Libravatar see: http://doc.gitlab.com/ce/customization/libravatar.html
   gravatar:
@@ -316,8 +329,6 @@ production: &base
     # path: /mnt/gitlab # Default: shared
 
 
-
-
   #
   # 4. Advanced settings
   # ==========================
@@ -418,6 +429,8 @@ test:
   <<: *base
   gravatar:
     enabled: true
+  lfs:
+    enabled: false
   gitlab:
     host: localhost
     port: 80
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 302124bd9771f1afe8754aa98e4ab15e1756f57b..62619241001731fc30d64de0224c83ede6342355 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -33,13 +33,15 @@ class Settings < Settingslogic
     end
 
     def build_gitlab_shell_ssh_path_prefix
+      user_host = "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}"
+
       if gitlab_shell.ssh_port != 22
-        "ssh://#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:#{gitlab_shell.ssh_port}/"
+        "ssh://#{user_host}:#{gitlab_shell.ssh_port}/"
       else
         if gitlab_shell.ssh_host.include? ':'
-          "[#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}]:"
+          "[#{user_host}]:"
         else
-          "#{gitlab_shell.ssh_user}@#{gitlab_shell.ssh_host}:"
+          "#{user_host}:"
         end
       end
     end
@@ -171,6 +173,7 @@ Settings.gitlab.default_projects_features['issues']         = true if Settings.g
 Settings.gitlab.default_projects_features['merge_requests'] = true if Settings.gitlab.default_projects_features['merge_requests'].nil?
 Settings.gitlab.default_projects_features['wiki']           = true if Settings.gitlab.default_projects_features['wiki'].nil?
 Settings.gitlab.default_projects_features['snippets']       = false if Settings.gitlab.default_projects_features['snippets'].nil?
+Settings.gitlab.default_projects_features['builds']         = true if Settings.gitlab.default_projects_features['builds'].nil?
 Settings.gitlab.default_projects_features['visibility_level']    = Settings.send(:verify_constant, Gitlab::VisibilityLevel, Settings.gitlab.default_projects_features['visibility_level'], Gitlab::VisibilityLevel::PRIVATE)
 Settings.gitlab['repository_downloads_path'] = File.join(Settings.shared['path'], 'cache/archive') if Settings.gitlab['repository_downloads_path'].nil?
 Settings.gitlab['restricted_signup_domains'] ||= []
@@ -186,7 +189,6 @@ Settings.gitlab_ci['all_broken_builds']     = true if Settings.gitlab_ci['all_br
 Settings.gitlab_ci['add_pusher']            = false if Settings.gitlab_ci['add_pusher'].nil?
 Settings.gitlab_ci['url']                   ||= Settings.send(:build_gitlab_ci_url)
 Settings.gitlab_ci['builds_path']           = File.expand_path(Settings.gitlab_ci['builds_path'] || "builds/", Rails.root)
-Settings.gitlab_ci['max_artifacts_size']    ||= 100 # in megabytes
 
 #
 # Reply by email
@@ -198,6 +200,21 @@ Settings.incoming_email['ssl']        = false if Settings.incoming_email['ssl'].
 Settings.incoming_email['start_tls']  = false if Settings.incoming_email['start_tls'].nil?
 Settings.incoming_email['mailbox']    = "inbox" if Settings.incoming_email['mailbox'].nil?
 
+#
+# Build Artifacts
+#
+Settings['artifacts'] ||= Settingslogic.new({})
+Settings.artifacts['enabled']      = true if Settings.artifacts['enabled'].nil?
+Settings.artifacts['path']         = File.expand_path(Settings.artifacts['path'] || File.join(Settings.shared['path'], "artifacts"), Rails.root)
+Settings.artifacts['max_size']    ||= 100 # in megabytes
+
+#
+# Git LFS
+#
+Settings['lfs'] ||= Settingslogic.new({})
+Settings.lfs['enabled']      = true if Settings.lfs['enabled'].nil?
+Settings.lfs['storage_path'] = File.expand_path(Settings.lfs['storage_path'] || File.join(Settings.shared['path'], "lfs-objects"), Rails.root)
+
 #
 # Gravatar
 #
@@ -278,3 +295,12 @@ if Rails.env.test?
   Settings.gitlab['default_can_create_group'] = true
   Settings.gitlab['default_can_create_team']  = false
 end
+
+# Force a refresh of application settings at startup
+begin
+  ApplicationSetting.expire
+  Ci::ApplicationSetting.expire
+rescue
+  # Gracefully handle when Redis is not available. For example,
+  # omnibus may fail here during assets:precompile.
+end
diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb
index 43adac8b2c6c2a80fba8bb41bbc3813407338d1f..54516e3f23da72aea2680c1122d3c22353e104c0 100644
--- a/config/initializers/cookies_serializer.rb
+++ b/config/initializers/cookies_serializer.rb
@@ -1,3 +1,3 @@
 # Be sure to restart your server when you modify this file.
 
-Gitlab::Application.config.action_dispatch.cookies_serializer = :hybrid
+Rails.application.config.action_dispatch.cookies_serializer = :hybrid
diff --git a/config/initializers/default_url_options.rb b/config/initializers/default_url_options.rb
index f9f88f95db98ce2c4d8a902dcada54a855968510..8fd27b1d88e4f80349314c6f016407822e2e5b53 100644
--- a/config/initializers/default_url_options.rb
+++ b/config/initializers/default_url_options.rb
@@ -8,4 +8,4 @@ unless Gitlab.config.gitlab_on_standard_port?
   default_url_options[:port] = Gitlab.config.gitlab.port
 end
 
-Gitlab::Application.routes.default_url_options = default_url_options
+Rails.application.routes.default_url_options = default_url_options
diff --git a/config/initializers/rack_attack.rb.example b/config/initializers/rack_attack.rb.example
index 2155ea145625eee86519d48cfc0a933669a5a7ed..b1bbcca1d615f8ab53c3332cdeecc365a4eb2e11 100644
--- a/config/initializers/rack_attack.rb.example
+++ b/config/initializers/rack_attack.rb.example
@@ -4,13 +4,13 @@
 # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
 
 paths_to_be_protected = [
-  "#{Gitlab::Application.config.relative_url_root}/users/password",
-  "#{Gitlab::Application.config.relative_url_root}/users/sign_in",
-  "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session.json",
-  "#{Gitlab::Application.config.relative_url_root}/api/#{API::API.version}/session",
-  "#{Gitlab::Application.config.relative_url_root}/users",
-  "#{Gitlab::Application.config.relative_url_root}/users/confirmation",
-  "#{Gitlab::Application.config.relative_url_root}/unsubscribes/"
+  "#{Rails.application.config.relative_url_root}/users/password",
+  "#{Rails.application.config.relative_url_root}/users/sign_in",
+  "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session.json",
+  "#{Rails.application.config.relative_url_root}/api/#{API::API.version}/session",
+  "#{Rails.application.config.relative_url_root}/users",
+  "#{Rails.application.config.relative_url_root}/users/confirmation",
+  "#{Rails.application.config.relative_url_root}/unsubscribes/"
 
 ]
 
diff --git a/config/initializers/rack_lineprof.rb b/config/initializers/rack_lineprof.rb
index f0c006d811bffe04c02572441f7d5cc329ba0255..22e77a32c614bffa2d0709d46298cf21e3da6d49 100644
--- a/config/initializers/rack_lineprof.rb
+++ b/config/initializers/rack_lineprof.rb
@@ -2,7 +2,7 @@
 # with darker backgrounds. This patch tweaks the colors a bit so the output is
 # actually readable.
 if Rails.env.development? and RUBY_ENGINE == 'ruby' and ENV['ENABLE_LINEPROF']
-  Gitlab::Application.config.middleware.use(Rack::Lineprof)
+  Rails.application.config.middleware.use(Rack::Lineprof)
 
   module Rack
     class Lineprof
diff --git a/config/initializers/secret_token.rb b/config/initializers/secret_token.rb
index 1b518c3becf51104c9c52f88413ae78ccf0c4d01..dae3a4a9a93f26da1b6c9289bf806639508b38c3 100644
--- a/config/initializers/secret_token.rb
+++ b/config/initializers/secret_token.rb
@@ -22,15 +22,15 @@ def find_secure_token
   end
 end
 
-Gitlab::Application.config.secret_token = find_secure_token
-Gitlab::Application.config.secret_key_base = find_secure_token
+Rails.application.config.secret_token = find_secure_token
+Rails.application.config.secret_key_base = find_secure_token
 
 # CI
 def generate_new_secure_token
   SecureRandom.hex(64)
 end
 
-if Gitlab::Application.secrets.db_key_base.blank?
+if Rails.application.secrets.db_key_base.blank?
   warn "Missing `db_key_base` for '#{Rails.env}' environment. The secrets will be generated and stored in `config/secrets.yml`"
 
   all_secrets = YAML.load_file('config/secrets.yml') if File.exist?('config/secrets.yml')
@@ -46,5 +46,5 @@ if Gitlab::Application.secrets.db_key_base.blank?
     file.write(YAML.dump(all_secrets))
   end
 
-  Gitlab::Application.secrets.db_key_base = env_secrets['db_key_base']
+  Rails.application.secrets.db_key_base = env_secrets['db_key_base']
 end
diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb
index 04ed9e90df5300329d980acd0d6d1f7d1736e369..d5208b8c93efc46bba8ff038697ed469d8a1256e 100644
--- a/config/initializers/session_store.rb
+++ b/config/initializers/session_store.rb
@@ -3,18 +3,21 @@
 require 'gitlab/current_settings'
 include Gitlab::CurrentSettings
 
-# allow it to fail: it may to do so when create_from_defaults is executed before migrations are actually done
+# allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
 begin
-  Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay
+  Settings.gitlab['session_expire_delay'] = current_application_settings.session_expire_delay || 10080
 rescue
+  Settings.gitlab['session_expire_delay'] ||= 10080
 end
 
-Gitlab::Application.config.session_store(
-  :redis_store, # Using the cookie_store would enable session replay attacks.
-  servers: Gitlab::Application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
-  key: '_gitlab_session',
-  secure: Gitlab.config.gitlab.https,
-  httponly: true,
-  expire_after: Settings.gitlab['session_expire_delay'] * 60,
-  path: (Gitlab::Application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root
-)
+unless Rails.env.test?
+  Gitlab::Application.config.session_store(
+    :redis_store, # Using the cookie_store would enable session replay attacks.
+    servers: Rails.application.config.cache_store[1].merge(namespace: 'session:gitlab'), # re-use the Redis config from the Rails cache store
+    key: '_gitlab_session',
+    secure: Gitlab.config.gitlab.https,
+    httponly: true,
+    expire_after: Settings.gitlab['session_expire_delay'] * 60,
+    path: (Rails.application.config.relative_url_root.nil?) ? '/' : Gitlab::Application.config.relative_url_root
+  )
+end
diff --git a/config/initializers/sherlock.rb b/config/initializers/sherlock.rb
index 42b0d78c85fae51d6c800ab2cc66b0e77afb75a7..8f2ababb712f93189f18fd4a9704f06e58d55492 100644
--- a/config/initializers/sherlock.rb
+++ b/config/initializers/sherlock.rb
@@ -1,5 +1,5 @@
 if Gitlab::Sherlock.enabled?
-  Gitlab::Application.configure do |config|
+  Rails.application.configure do |config|
     config.middleware.use(Gitlab::Sherlock::Middleware)
   end
 end
diff --git a/config/initializers/smtp_settings.rb.sample b/config/initializers/smtp_settings.rb.sample
index 25ec247a0954b709c18327a1fe8f16411a96d05d..ec182502d4e38afdf0453eafefce3c9ccf0f92e5 100644
--- a/config/initializers/smtp_settings.rb.sample
+++ b/config/initializers/smtp_settings.rb.sample
@@ -8,7 +8,7 @@
 # If you change this file in a Merge Request, please also create a Merge Request on https://gitlab.com/gitlab-org/omnibus-gitlab/merge_requests
 
 if Rails.env.production?
-  Gitlab::Application.config.action_mailer.delivery_method = :smtp
+  Rails.application.config.action_mailer.delivery_method = :smtp
 
   ActionMailer::Base.smtp_settings = {
     address: "email.server.com",
diff --git a/config/initializers/static_files.rb b/config/initializers/static_files.rb
index e6d5600edb7e4f5b4f1c75cbb6915a29c311d67a..d6dbf8b9fbfca8d9edc43a8e09a650cd1a3c5904 100644
--- a/config/initializers/static_files.rb
+++ b/config/initializers/static_files.rb
@@ -1,6 +1,6 @@
-app = Gitlab::Application
+app = Rails.application
 
-if app.config.serve_static_assets
+if app.config.serve_static_files
   # The `ActionDispatch::Static` middleware intercepts requests for static files 
   # by checking if they exist in the `/public` directory. 
   # We're replacing it with our `Gitlab::Middleware::Static` that does the same,
diff --git a/config/locales/sherlock.en.yml b/config/locales/sherlock.en.yml
index 683b09dc3294d5710850802193572470bee0f5d9..f24b825f585410490fb44456aef97cbc0e1033d1 100644
--- a/config/locales/sherlock.en.yml
+++ b/config/locales/sherlock.en.yml
@@ -35,3 +35,4 @@ en:
     events: Events
     percent: '%'
     count: Count
+    query_time: Query Time
diff --git a/config/routes.rb b/config/routes.rb
index c892c034f0102b24b1972ac49be0a942c7f1f762..5c114452a3f4cb4ff94f1f3ca374c1652a66e3d9 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,7 +1,7 @@
 require 'sidekiq/web'
 require 'api/api'
 
-Gitlab::Application.routes.draw do
+Rails.application.routes.draw do
   if Gitlab::Sherlock.enabled?
     namespace :sherlock do
       resources :transactions, only: [:index, :show] do
@@ -32,7 +32,6 @@ Gitlab::Application.routes.draw do
         get :status, to: 'projects#badge'
         get :integration
         post :toggle_shared_runners
-        get :dumped_yaml
       end
 
       resources :runner_projects, only: [:create, :destroy]
@@ -94,7 +93,7 @@ Gitlab::Application.routes.draw do
   end
 
   # Enable Grack support
-  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post]
+  mount Grack::AuthSpawner, at: '/', constraints: lambda { |request| /[-\/\w\.]+\.git\//.match(request.path_info) }, via: [:get, :post, :put]
 
   # Help
   get 'help'                  => 'help#index'
@@ -223,6 +222,8 @@ Gitlab::Application.routes.draw do
       resources :keys, only: [:show, :destroy]
       resources :identities, only: [:index, :edit, :update, :destroy]
 
+      delete 'stop_impersonation' => 'impersonation#destroy', on: :collection
+
       member do
         get :projects
         get :keys
@@ -232,7 +233,7 @@ Gitlab::Application.routes.draw do
         put :unblock
         put :unlock
         put :confirm
-        post :login_as
+        post 'impersonate' => 'impersonation#create'
         patch :disable_two_factor
         delete 'remove/:email_id', action: 'remove_email', as: 'remove_email'
       end
@@ -367,7 +368,7 @@ Gitlab::Application.routes.draw do
       end
 
       resource :avatar, only: [:destroy]
-      resources :milestones, only: [:index, :show, :update]
+      resources :milestones, only: [:index, :show, :update, :new, :create]
     end
   end
 
@@ -663,6 +664,10 @@ Gitlab::Application.routes.draw do
           member do
             delete :delete_attachment
           end
+
+          collection do
+            post :award_toggle
+          end
         end
 
         resources :uploads, only: [:create] do
diff --git a/db/migrate/20151103134857_create_lfs_objects.rb b/db/migrate/20151103134857_create_lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2d04c170a88c9f9c0eb4d5bcb2392b5b7d50ff66
--- /dev/null
+++ b/db/migrate/20151103134857_create_lfs_objects.rb
@@ -0,0 +1,10 @@
+class CreateLfsObjects < ActiveRecord::Migration
+  def change
+    create_table :lfs_objects do |t|
+      t.string :oid, null: false, unique: true
+      t.integer :size, null: false
+
+      t.timestamps
+    end
+  end
+end
diff --git a/db/migrate/20151103134958_create_lfs_objects_projects.rb b/db/migrate/20151103134958_create_lfs_objects_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f3f58b931ece32a085ab123006f210e6727fb0b4
--- /dev/null
+++ b/db/migrate/20151103134958_create_lfs_objects_projects.rb
@@ -0,0 +1,12 @@
+class CreateLfsObjectsProjects < ActiveRecord::Migration
+  def change
+    create_table :lfs_objects_projects do |t|
+      t.integer :lfs_object_id, null: false
+      t.integer :project_id, null: false
+
+      t.timestamps
+    end
+
+    add_index :lfs_objects_projects, :project_id
+  end
+end
diff --git a/db/migrate/20151104105513_add_file_to_lfs_objects.rb b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7c57f3f0df655017afcd6ed4acba5af3b415973a
--- /dev/null
+++ b/db/migrate/20151104105513_add_file_to_lfs_objects.rb
@@ -0,0 +1,5 @@
+class AddFileToLfsObjects < ActiveRecord::Migration
+  def change
+    add_column :lfs_objects, :file, :string
+  end
+end
diff --git a/db/migrate/20151106000015_add_is_award_to_notes.rb b/db/migrate/20151106000015_add_is_award_to_notes.rb
new file mode 100644
index 0000000000000000000000000000000000000000..02b271637e960c9214481e0e4eed4287f49d81c0
--- /dev/null
+++ b/db/migrate/20151106000015_add_is_award_to_notes.rb
@@ -0,0 +1,6 @@
+class AddIsAwardToNotes < ActiveRecord::Migration
+  def change
+    add_column :notes, :is_award, :boolean, default: false, null: false
+    add_index :notes, :is_award
+  end
+end
diff --git a/db/migrate/20151109134526_add_issues_state_index.rb b/db/migrate/20151109134526_add_issues_state_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1c4d2e30171886aadd04fd34f482843d9ca9d7aa
--- /dev/null
+++ b/db/migrate/20151109134526_add_issues_state_index.rb
@@ -0,0 +1,5 @@
+class AddIssuesStateIndex < ActiveRecord::Migration
+  def change
+    add_index :issues, :state
+  end
+end
diff --git a/db/migrate/20151109134916_add_projects_visibility_level_index.rb b/db/migrate/20151109134916_add_projects_visibility_level_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..600b4bafd98482679edca82ecdde5624ffcdd679
--- /dev/null
+++ b/db/migrate/20151109134916_add_projects_visibility_level_index.rb
@@ -0,0 +1,5 @@
+class AddProjectsVisibilityLevelIndex < ActiveRecord::Migration
+  def change
+    add_index :projects, :visibility_level
+  end
+end
diff --git a/db/migrate/20151110125604_add_import_error_to_project.rb b/db/migrate/20151110125604_add_import_error_to_project.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7fc990f8d0a9f56316c566ae421b158e8d26d268
--- /dev/null
+++ b/db/migrate/20151110125604_add_import_error_to_project.rb
@@ -0,0 +1,5 @@
+class AddImportErrorToProject < ActiveRecord::Migration
+  def change
+    add_column :projects, :import_error, :text
+  end
+end
diff --git a/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d10f1f6e6054ab5f5561082fe95b0039695f8aa1
--- /dev/null
+++ b/db/migrate/20151114113410_add_index_for_lfs_oid_and_size.rb
@@ -0,0 +1,6 @@
+class AddIndexForLfsOidAndSize < ActiveRecord::Migration
+  def change
+    add_index :lfs_objects, :oid
+    add_index :lfs_objects, [:oid, :size]
+  end
+end
diff --git a/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..41b93da0a8699036002deea43de92c2cb2bf2ad5
--- /dev/null
+++ b/db/migrate/20151116144118_add_unique_for_lfs_oid_index.rb
@@ -0,0 +1,7 @@
+class AddUniqueForLfsOidIndex < ActiveRecord::Migration
+  def change
+    remove_index :lfs_objects, :oid
+    remove_index :lfs_objects, [:oid, :size]
+    add_index :lfs_objects, :oid, unique: true
+  end
+end
diff --git a/db/migrate/20151118162244_add_projects_public_index.rb b/db/migrate/20151118162244_add_projects_public_index.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fded70e3c0c8c38c67f7d30e4be82d11039be991
--- /dev/null
+++ b/db/migrate/20151118162244_add_projects_public_index.rb
@@ -0,0 +1,5 @@
+class AddProjectsPublicIndex < ActiveRecord::Migration
+  def change
+    add_index :namespaces, :public
+  end
+end
diff --git a/db/schema.rb b/db/schema.rb
index f631d73f334ef2a6df33c3ff28087608704c73cc..fbcb711e569ff687174fbbb3f901f4d62f399a51 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,12 +11,12 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20151109100728) do
+ActiveRecord::Schema.define(version: 20151118162244) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
 
-  create_table "abuse_reports", force: true do |t|
+  create_table "abuse_reports", force: :cascade do |t|
     t.integer  "reporter_id"
     t.integer  "user_id"
     t.text     "message"
@@ -24,7 +24,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.datetime "updated_at"
   end
 
-  create_table "application_settings", force: true do |t|
+  create_table "application_settings", force: :cascade do |t|
     t.integer  "default_projects_limit"
     t.boolean  "signup_enabled"
     t.boolean  "signin_enabled"
@@ -51,7 +51,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.integer  "max_artifacts_size",           default: 100,   null: false
   end
 
-  create_table "audit_events", force: true do |t|
+  create_table "audit_events", force: :cascade do |t|
     t.integer  "author_id",   null: false
     t.string   "type",        null: false
     t.integer  "entity_id",   null: false
@@ -65,7 +65,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "audit_events", ["entity_id", "entity_type"], name: "index_audit_events_on_entity_id_and_entity_type", using: :btree
   add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree
 
-  create_table "broadcast_messages", force: true do |t|
+  create_table "broadcast_messages", force: :cascade do |t|
     t.text     "message",    null: false
     t.datetime "starts_at"
     t.datetime "ends_at"
@@ -76,14 +76,14 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.string   "font"
   end
 
-  create_table "ci_application_settings", force: true do |t|
+  create_table "ci_application_settings", force: :cascade do |t|
     t.boolean  "all_broken_builds"
     t.boolean  "add_pusher"
     t.datetime "created_at"
     t.datetime "updated_at"
   end
 
-  create_table "ci_builds", force: true do |t|
+  create_table "ci_builds", force: :cascade do |t|
     t.integer  "project_id"
     t.string   "status"
     t.datetime "finished_at"
@@ -123,7 +123,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_builds", ["status"], name: "index_ci_builds_on_status", using: :btree
   add_index "ci_builds", ["type"], name: "index_ci_builds_on_type", using: :btree
 
-  create_table "ci_commits", force: true do |t|
+  create_table "ci_commits", force: :cascade do |t|
     t.integer  "project_id"
     t.string   "ref"
     t.string   "sha"
@@ -144,7 +144,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_commits", ["project_id"], name: "index_ci_commits_on_project_id", using: :btree
   add_index "ci_commits", ["sha"], name: "index_ci_commits_on_sha", using: :btree
 
-  create_table "ci_events", force: true do |t|
+  create_table "ci_events", force: :cascade do |t|
     t.integer  "project_id"
     t.integer  "user_id"
     t.integer  "is_admin"
@@ -157,7 +157,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_events", ["is_admin"], name: "index_ci_events_on_is_admin", using: :btree
   add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree
 
-  create_table "ci_jobs", force: true do |t|
+  create_table "ci_jobs", force: :cascade do |t|
     t.integer  "project_id",                          null: false
     t.text     "commands"
     t.boolean  "active",         default: true,       null: false
@@ -174,7 +174,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_jobs", ["deleted_at"], name: "index_ci_jobs_on_deleted_at", using: :btree
   add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree
 
-  create_table "ci_projects", force: true do |t|
+  create_table "ci_projects", force: :cascade do |t|
     t.string   "name"
     t.integer  "timeout",                  default: 3600,  null: false
     t.datetime "created_at"
@@ -200,7 +200,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_projects", ["gitlab_id"], name: "index_ci_projects_on_gitlab_id", using: :btree
   add_index "ci_projects", ["shared_runners_enabled"], name: "index_ci_projects_on_shared_runners_enabled", using: :btree
 
-  create_table "ci_runner_projects", force: true do |t|
+  create_table "ci_runner_projects", force: :cascade do |t|
     t.integer  "runner_id",  null: false
     t.integer  "project_id", null: false
     t.datetime "created_at"
@@ -210,7 +210,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_runner_projects", ["project_id"], name: "index_ci_runner_projects_on_project_id", using: :btree
   add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
 
-  create_table "ci_runners", force: true do |t|
+  create_table "ci_runners", force: :cascade do |t|
     t.string   "token"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -225,7 +225,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.string   "architecture"
   end
 
-  create_table "ci_services", force: true do |t|
+  create_table "ci_services", force: :cascade do |t|
     t.string   "type"
     t.string   "title"
     t.integer  "project_id",                 null: false
@@ -237,7 +237,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree
 
-  create_table "ci_sessions", force: true do |t|
+  create_table "ci_sessions", force: :cascade do |t|
     t.string   "session_id", null: false
     t.text     "data"
     t.datetime "created_at"
@@ -247,7 +247,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_sessions", ["session_id"], name: "index_ci_sessions_on_session_id", using: :btree
   add_index "ci_sessions", ["updated_at"], name: "index_ci_sessions_on_updated_at", using: :btree
 
-  create_table "ci_taggings", force: true do |t|
+  create_table "ci_taggings", force: :cascade do |t|
     t.integer  "tag_id"
     t.integer  "taggable_id"
     t.string   "taggable_type"
@@ -260,14 +260,14 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "ci_taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "ci_taggings_idx", unique: true, using: :btree
   add_index "ci_taggings", ["taggable_id", "taggable_type", "context"], name: "index_ci_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
 
-  create_table "ci_tags", force: true do |t|
+  create_table "ci_tags", force: :cascade do |t|
     t.string  "name"
     t.integer "taggings_count", default: 0
   end
 
   add_index "ci_tags", ["name"], name: "index_ci_tags_on_name", unique: true, using: :btree
 
-  create_table "ci_trigger_requests", force: true do |t|
+  create_table "ci_trigger_requests", force: :cascade do |t|
     t.integer  "trigger_id", null: false
     t.text     "variables"
     t.datetime "created_at"
@@ -275,7 +275,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.integer  "commit_id"
   end
 
-  create_table "ci_triggers", force: true do |t|
+  create_table "ci_triggers", force: :cascade do |t|
     t.string   "token"
     t.integer  "project_id", null: false
     t.datetime "deleted_at"
@@ -285,7 +285,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "ci_triggers", ["deleted_at"], name: "index_ci_triggers_on_deleted_at", using: :btree
 
-  create_table "ci_variables", force: true do |t|
+  create_table "ci_variables", force: :cascade do |t|
     t.integer "project_id",           null: false
     t.string  "key"
     t.text    "value"
@@ -296,14 +296,14 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "ci_variables", ["project_id"], name: "index_ci_variables_on_project_id", using: :btree
 
-  create_table "ci_web_hooks", force: true do |t|
+  create_table "ci_web_hooks", force: :cascade do |t|
     t.string   "url",        null: false
     t.integer  "project_id", null: false
     t.datetime "created_at"
     t.datetime "updated_at"
   end
 
-  create_table "deploy_keys_projects", force: true do |t|
+  create_table "deploy_keys_projects", force: :cascade do |t|
     t.integer  "deploy_key_id", null: false
     t.integer  "project_id",    null: false
     t.datetime "created_at"
@@ -312,7 +312,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
 
-  create_table "emails", force: true do |t|
+  create_table "emails", force: :cascade do |t|
     t.integer  "user_id",    null: false
     t.string   "email",      null: false
     t.datetime "created_at"
@@ -322,7 +322,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "emails", ["email"], name: "index_emails_on_email", unique: true, using: :btree
   add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
 
-  create_table "events", force: true do |t|
+  create_table "events", force: :cascade do |t|
     t.string   "target_type"
     t.integer  "target_id"
     t.string   "title"
@@ -341,7 +341,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "events", ["target_id"], name: "index_events_on_target_id", using: :btree
   add_index "events", ["target_type"], name: "index_events_on_target_type", using: :btree
 
-  create_table "forked_project_links", force: true do |t|
+  create_table "forked_project_links", force: :cascade do |t|
     t.integer  "forked_to_project_id",   null: false
     t.integer  "forked_from_project_id", null: false
     t.datetime "created_at"
@@ -350,7 +350,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "forked_project_links", ["forked_to_project_id"], name: "index_forked_project_links_on_forked_to_project_id", unique: true, using: :btree
 
-  create_table "identities", force: true do |t|
+  create_table "identities", force: :cascade do |t|
     t.string   "extern_uid"
     t.string   "provider"
     t.integer  "user_id"
@@ -361,7 +361,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "identities", ["created_at", "id"], name: "index_identities_on_created_at_and_id", using: :btree
   add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
 
-  create_table "issues", force: true do |t|
+  create_table "issues", force: :cascade do |t|
     t.string   "title"
     t.integer  "assignee_id"
     t.integer  "author_id"
@@ -384,9 +384,10 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "issues", ["milestone_id"], name: "index_issues_on_milestone_id", using: :btree
   add_index "issues", ["project_id", "iid"], name: "index_issues_on_project_id_and_iid", unique: true, using: :btree
   add_index "issues", ["project_id"], name: "index_issues_on_project_id", using: :btree
+  add_index "issues", ["state"], name: "index_issues_on_state", using: :btree
   add_index "issues", ["title"], name: "index_issues_on_title", using: :btree
 
-  create_table "keys", force: true do |t|
+  create_table "keys", force: :cascade do |t|
     t.integer  "user_id"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -400,7 +401,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree
   add_index "keys", ["user_id"], name: "index_keys_on_user_id", using: :btree
 
-  create_table "label_links", force: true do |t|
+  create_table "label_links", force: :cascade do |t|
     t.integer  "label_id"
     t.integer  "target_id"
     t.string   "target_type"
@@ -411,7 +412,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "label_links", ["label_id"], name: "index_label_links_on_label_id", using: :btree
   add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
 
-  create_table "labels", force: true do |t|
+  create_table "labels", force: :cascade do |t|
     t.string   "title"
     t.string   "color"
     t.integer  "project_id"
@@ -422,7 +423,26 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
 
-  create_table "members", force: true do |t|
+  create_table "lfs_objects", force: :cascade do |t|
+    t.string   "oid",        null: false
+    t.integer  "size",       null: false
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.string   "file"
+  end
+
+  add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
+
+  create_table "lfs_objects_projects", force: :cascade do |t|
+    t.integer  "lfs_object_id", null: false
+    t.integer  "project_id",    null: false
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
+  add_index "lfs_objects_projects", ["project_id"], name: "index_lfs_objects_projects_on_project_id", using: :btree
+
+  create_table "members", force: :cascade do |t|
     t.integer  "access_level",       null: false
     t.integer  "source_id",          null: false
     t.string   "source_type",        null: false
@@ -444,7 +464,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "members", ["type"], name: "index_members_on_type", using: :btree
   add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
 
-  create_table "merge_request_diffs", force: true do |t|
+  create_table "merge_request_diffs", force: :cascade do |t|
     t.string   "state"
     t.text     "st_commits"
     t.text     "st_diffs"
@@ -455,7 +475,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "merge_request_diffs", ["merge_request_id"], name: "index_merge_request_diffs_on_merge_request_id", unique: true, using: :btree
 
-  create_table "merge_requests", force: true do |t|
+  create_table "merge_requests", force: :cascade do |t|
     t.string   "target_branch",                 null: false
     t.string   "source_branch",                 null: false
     t.integer  "source_project_id",             null: false
@@ -487,7 +507,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "merge_requests", ["target_project_id", "iid"], name: "index_merge_requests_on_target_project_id_and_iid", unique: true, using: :btree
   add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
 
-  create_table "milestones", force: true do |t|
+  create_table "milestones", force: :cascade do |t|
     t.string   "title",       null: false
     t.integer  "project_id",  null: false
     t.text     "description"
@@ -503,7 +523,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "milestones", ["project_id", "iid"], name: "index_milestones_on_project_id_and_iid", unique: true, using: :btree
   add_index "milestones", ["project_id"], name: "index_milestones_on_project_id", using: :btree
 
-  create_table "namespaces", force: true do |t|
+  create_table "namespaces", force: :cascade do |t|
     t.string   "name",                        null: false
     t.string   "path",                        null: false
     t.integer  "owner_id"
@@ -519,9 +539,10 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "namespaces", ["name"], name: "index_namespaces_on_name", unique: true, using: :btree
   add_index "namespaces", ["owner_id"], name: "index_namespaces_on_owner_id", using: :btree
   add_index "namespaces", ["path"], name: "index_namespaces_on_path", unique: true, using: :btree
+  add_index "namespaces", ["public"], name: "index_namespaces_on_public", using: :btree
   add_index "namespaces", ["type"], name: "index_namespaces_on_type", using: :btree
 
-  create_table "notes", force: true do |t|
+  create_table "notes", force: :cascade do |t|
     t.text     "note"
     t.string   "noteable_type"
     t.integer  "author_id"
@@ -535,12 +556,14 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.boolean  "system",        default: false, null: false
     t.text     "st_diff"
     t.integer  "updated_by_id"
+    t.boolean  "is_award",      default: false, null: false
   end
 
   add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
   add_index "notes", ["commit_id"], name: "index_notes_on_commit_id", using: :btree
   add_index "notes", ["created_at", "id"], name: "index_notes_on_created_at_and_id", using: :btree
   add_index "notes", ["created_at"], name: "index_notes_on_created_at", using: :btree
+  add_index "notes", ["is_award"], name: "index_notes_on_is_award", using: :btree
   add_index "notes", ["line_code"], name: "index_notes_on_line_code", using: :btree
   add_index "notes", ["noteable_id", "noteable_type"], name: "index_notes_on_noteable_id_and_noteable_type", using: :btree
   add_index "notes", ["noteable_type"], name: "index_notes_on_noteable_type", using: :btree
@@ -548,7 +571,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "notes", ["project_id"], name: "index_notes_on_project_id", using: :btree
   add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
 
-  create_table "oauth_access_grants", force: true do |t|
+  create_table "oauth_access_grants", force: :cascade do |t|
     t.integer  "resource_owner_id", null: false
     t.integer  "application_id",    null: false
     t.string   "token",             null: false
@@ -561,7 +584,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
 
-  create_table "oauth_access_tokens", force: true do |t|
+  create_table "oauth_access_tokens", force: :cascade do |t|
     t.integer  "resource_owner_id"
     t.integer  "application_id"
     t.string   "token",             null: false
@@ -576,7 +599,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "oauth_access_tokens", ["resource_owner_id"], name: "index_oauth_access_tokens_on_resource_owner_id", using: :btree
   add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
 
-  create_table "oauth_applications", force: true do |t|
+  create_table "oauth_applications", force: :cascade do |t|
     t.string   "name",                      null: false
     t.string   "uid",                       null: false
     t.string   "secret",                    null: false
@@ -591,12 +614,12 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
   add_index "oauth_applications", ["uid"], name: "index_oauth_applications_on_uid", unique: true, using: :btree
 
-  create_table "project_import_data", force: true do |t|
+  create_table "project_import_data", force: :cascade do |t|
     t.integer "project_id"
     t.text    "data"
   end
 
-  create_table "projects", force: true do |t|
+  create_table "projects", force: :cascade do |t|
     t.string   "name"
     t.string   "path"
     t.text     "description"
@@ -622,6 +645,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
     t.string   "import_type"
     t.string   "import_source"
     t.integer  "commit_count",           default: 0
+    t.text     "import_error"
   end
 
   add_index "projects", ["created_at", "id"], name: "index_projects_on_created_at_and_id", using: :btree
@@ -630,8 +654,9 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "projects", ["namespace_id"], name: "index_projects_on_namespace_id", using: :btree
   add_index "projects", ["path"], name: "index_projects_on_path", using: :btree
   add_index "projects", ["star_count"], name: "index_projects_on_star_count", using: :btree
+  add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
 
-  create_table "protected_branches", force: true do |t|
+  create_table "protected_branches", force: :cascade do |t|
     t.integer  "project_id",                          null: false
     t.string   "name",                                null: false
     t.datetime "created_at"
@@ -641,7 +666,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
 
-  create_table "releases", force: true do |t|
+  create_table "releases", force: :cascade do |t|
     t.string   "tag"
     t.text     "description"
     t.integer  "project_id"
@@ -652,7 +677,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "releases", ["project_id", "tag"], name: "index_releases_on_project_id_and_tag", using: :btree
   add_index "releases", ["project_id"], name: "index_releases_on_project_id", using: :btree
 
-  create_table "sent_notifications", force: true do |t|
+  create_table "sent_notifications", force: :cascade do |t|
     t.integer "project_id"
     t.integer "noteable_id"
     t.string  "noteable_type"
@@ -664,7 +689,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
 
-  create_table "services", force: true do |t|
+  create_table "services", force: :cascade do |t|
     t.string   "type"
     t.string   "title"
     t.integer  "project_id"
@@ -684,7 +709,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "services", ["project_id"], name: "index_services_on_project_id", using: :btree
   add_index "services", ["template"], name: "index_services_on_template", using: :btree
 
-  create_table "snippets", force: true do |t|
+  create_table "snippets", force: :cascade do |t|
     t.string   "title"
     t.text     "content"
     t.integer  "author_id",                    null: false
@@ -704,7 +729,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "snippets", ["project_id"], name: "index_snippets_on_project_id", using: :btree
   add_index "snippets", ["visibility_level"], name: "index_snippets_on_visibility_level", using: :btree
 
-  create_table "subscriptions", force: true do |t|
+  create_table "subscriptions", force: :cascade do |t|
     t.integer  "user_id"
     t.integer  "subscribable_id"
     t.string   "subscribable_type"
@@ -715,7 +740,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
 
   add_index "subscriptions", ["subscribable_id", "subscribable_type", "user_id"], name: "subscriptions_user_id_and_ref_fields", unique: true, using: :btree
 
-  create_table "taggings", force: true do |t|
+  create_table "taggings", force: :cascade do |t|
     t.integer  "tag_id"
     t.integer  "taggable_id"
     t.string   "taggable_type"
@@ -728,14 +753,14 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true, using: :btree
   add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context", using: :btree
 
-  create_table "tags", force: true do |t|
+  create_table "tags", force: :cascade do |t|
     t.string  "name"
     t.integer "taggings_count", default: 0
   end
 
   add_index "tags", ["name"], name: "index_tags_on_name", unique: true, using: :btree
 
-  create_table "users", force: true do |t|
+  create_table "users", force: :cascade do |t|
     t.string   "email",                      default: "",    null: false
     t.string   "encrypted_password",         default: "",    null: false
     t.string   "reset_password_token"
@@ -801,7 +826,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
   add_index "users", ["username"], name: "index_users_on_username", using: :btree
 
-  create_table "users_star_projects", force: true do |t|
+  create_table "users_star_projects", force: :cascade do |t|
     t.integer  "project_id", null: false
     t.integer  "user_id",    null: false
     t.datetime "created_at"
@@ -812,7 +837,7 @@ ActiveRecord::Schema.define(version: 20151109100728) do
   add_index "users_star_projects", ["user_id", "project_id"], name: "index_users_star_projects_on_user_id_and_project_id", unique: true, using: :btree
   add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
 
-  create_table "web_hooks", force: true do |t|
+  create_table "web_hooks", force: :cascade do |t|
     t.string   "url"
     t.integer  "project_id"
     t.datetime "created_at"
diff --git a/doc/README.md b/doc/README.md
index 0f6866475f716b77b8f7a095f6af0db6d7c78661..58ab5dd08e0b2771a84285e65762d2dead3122a4 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -50,6 +50,7 @@
 - [Welcome message](customization/welcome_message.md) Add a custom welcome message to the sign-in page.
 - [Reply by email](incoming_email/README.md) Allow users to comment on issues and merge requests by replying to notification emails.
 - [Migrate GitLab CI to CE/EE](migrate_ci_to_ce/README.md) Follow this guide to migrate your existing GitLab CI data to GitLab CE/EE.
+- [Git LFS configuration](workflow/lfs/lfs_administration.md)
 
 ## Contributor documentation
 
diff --git a/doc/api/README.md b/doc/api/README.md
index 6b8528de50c633f72ebd1d0fd314f96110bf0d04..25a31b235cc77dc195bea89b70e02a7c106b34c9 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -10,6 +10,7 @@
 - [Repositories](repositories.md)
 - [Repository Files](repository_files.md)
 - [Commits](commits.md)
+- [Tags](tags.md)
 - [Branches](branches.md)
 - [Merge Requests](merge_requests.md)
 - [Issues](issues.md)
diff --git a/doc/api/commits.md b/doc/api/commits.md
index 8e4a0ee1b82b4e2e53963c74001808bd4ca549a4..93d62b751e66ab0743e7ab5a0fa3b324952f371d 100644
--- a/doc/api/commits.md
+++ b/doc/api/commits.md
@@ -188,7 +188,7 @@ Parameters:
     "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"",
+    "started_at": "2015-10-12T09:47:16.250Z",
     "finished_at": "2015-10-12T09:47:16.262Z",
     "author": {
       "id": 1,
@@ -228,7 +228,7 @@ POST /projects/:id/statuses/:sha
   "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"",
+  "started_at": "2015-10-12T09:47:16.250Z",
   "finished_at": "2015-10-12T09:47:16.262Z",
   "author": {
     "id": 1,
diff --git a/doc/api/merge_requests.md b/doc/api/merge_requests.md
index ffa7f2cdf14d388d6c1bfaff2e3d73863168c73a..0cef09d5b271fd42d2f621d9096ae3a0b394756f 100644
--- a/doc/api/merge_requests.md
+++ b/doc/api/merge_requests.md
@@ -3,8 +3,9 @@
 ## List merge requests
 
 Get all merge requests for this project. 
-The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`). 
-The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests.
+The `state` parameter can be used to get only merge requests with a given state (`opened`, `closed`, or `merged`) or all of them (`all`).
+The pagination parameters `page` and `per_page` can be used to restrict the list of merge requests. With GitLab 8.2 the return fields `upvotes` and
+`downvotes` are deprecated and always return `0`.
 
 ```
 GET /projects/:id/merge_requests
@@ -57,7 +58,7 @@ Parameters:
 
 ## Get single MR
 
-Shows information about a single merge request.
+Shows information about a single merge request. With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and always return `0`.
 
 ```
 GET /projects/:id/merge_request/:merge_request_id
@@ -102,7 +103,9 @@ Parameters:
 
 ## Get single MR changes
 
-Shows information about the merge request including its files and changes
+Shows information about the merge request including its files and changes.
+With GitLab 8.2 the return fields `upvotes` and `downvotes` are deprecated and
+always return `0`.
 
 ```
 GET /projects/:id/merge_request/:merge_request_id/changes
@@ -173,7 +176,8 @@ Parameters:
 
 ## Create MR
 
-Creates a new merge request.
+Creates a new merge request. With GitLab 8.2 the return fields `upvotes` and `
+downvotes` are deprecated and always return `0`.
 
 ```
 POST /projects/:id/merge_requests
@@ -225,7 +229,8 @@ If an error occurs, an error number and a message explaining the reason is retur
 
 ## Update MR
 
-Updates an existing merge request. You can change the target branch, title, or even close the MR.
+Updates an existing merge request. You can change the target branch, title, or even close the MR. With GitLab 8.2 the return fields `upvotes` and `downvotes`
+are deprecated and always return `0`.
 
 ```
 PUT /projects/:id/merge_request/:merge_request_id
@@ -276,7 +281,8 @@ If an error occurs, an error number and a message explaining the reason is retur
 
 ## Accept MR
 
-Merge changes submitted with MR using this API.
+Merge changes submitted with MR using this API. With GitLab 8.2 the return
+fields `upvotes` and `downvotes` are deprecated and always return `0`.
 
 If merge success you get `200 OK`.
 
diff --git a/doc/api/notes.md b/doc/api/notes.md
index c683cb883d42505995168da24aafda61654293a8..e7f299c099417eecb30d0434a4b7f85c19d9052e 100644
--- a/doc/api/notes.md
+++ b/doc/api/notes.md
@@ -6,7 +6,8 @@ Notes are comments on snippets, issues or merge requests.
 
 ### List project issue notes
 
-Gets a list of all notes for a single issue.
+Gets a list of all notes for a single issue. With GitLab 8.2 the return fields
+`upvote` and `downvote` are deprecated and always return `false`.
 
 ```
 GET /projects/:id/issues/:issue_id/notes
@@ -49,7 +50,7 @@ Parameters:
       "created_at": "2013-09-30T13:46:01Z"
     },
     "created_at": "2013-10-02T09:56:03Z",
-    "system": false,
+    "system": true,
     "upvote": false,
     "downvote": false
   }
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 9648585703530f6be3923a651ec7ddd4a9b5912b..755cc6525c2d2fea7e84422b06c9ecfbe7b2ad85 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -60,6 +60,7 @@ Parameters:
     "path_with_namespace": "diaspora/diaspora-client",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13: 46: 02Z",
@@ -101,6 +102,7 @@ Parameters:
     "path_with_namespace": "brightbox/puppet",
     "issues_enabled": true,
     "merge_requests_enabled": true,
+    "builds_enabled": true,
     "wiki_enabled": true,
     "snippets_enabled": false,
     "created_at": "2013-09-30T13:46:02Z",
@@ -191,6 +193,7 @@ Parameters:
   "path_with_namespace": "diaspora/diaspora-project-site",
   "issues_enabled": true,
   "merge_requests_enabled": true,
+  "builds_enabled": true,
   "wiki_enabled": true,
   "snippets_enabled": false,
   "created_at": "2013-09-30T13: 46: 02Z",
@@ -312,6 +315,7 @@ Parameters:
 - `description` (optional) - short project description
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -334,6 +338,7 @@ Parameters:
 - `default_branch` (optional) - 'master' by default
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
@@ -357,6 +362,7 @@ Parameters:
 - `default_branch` (optional)
 - `issues_enabled` (optional)
 - `merge_requests_enabled` (optional)
+- `builds_enabled` (optional)
 - `wiki_enabled` (optional)
 - `snippets_enabled` (optional)
 - `public` (optional) - if `true` same as setting visibility_level = 20
diff --git a/doc/api/repositories.md b/doc/api/repositories.md
index 331674538028b4d4cbc5d85dad52ccce25fad98f..b6cca5d4e2ad6892a00c8bc11503d8738a68fa8d 100644
--- a/doc/api/repositories.md
+++ b/doc/api/repositories.md
@@ -1,79 +1,5 @@
 # Repositories
 
-## List project repository tags
-
-Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
-
-```
-GET /projects/:id/repository/tags
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-
-```json
-[
-  {
-    "commit": {
-      "author_name": "John Smith",
-      "author_email": "john@example.com",
-      "authored_date": "2012-05-28T04:42:42-07:00",
-      "committed_date": "2012-05-28T04:42:42-07:00",
-      "committer_name": "Jack Smith",
-      "committer_email": "jack@example.com",
-      "id": "2695effb5807a22ff3d138d593fd856244e155e7",
-      "message": "Initial commit",
-      "parents_ids": [
-        "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
-      ]
-    },
-    "name": "v1.0.0",
-    "message": null
-  }
-]
-```
-
-## Create a new tag
-
-Creates new tag in the repository that points to the supplied ref.
-
-```
-POST /projects/:id/repository/tags
-```
-
-Parameters:
-
-- `id` (required) - The ID of a project
-- `tag_name` (required) - The name of a tag
-- `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
-- `message` (optional) - Creates annotated tag.
-
-```json
-{
-  "commit": {
-    "author_name": "John Smith",
-    "author_email": "john@example.com",
-    "authored_date": "2012-05-28T04:42:42-07:00",
-    "committed_date": "2012-05-28T04:42:42-07:00",
-    "committer_name": "Jack Smith",
-    "committer_email": "jack@example.com",
-    "id": "2695effb5807a22ff3d138d593fd856244e155e7",
-    "message": "Initial commit",
-    "parents_ids": [
-      "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
-    ]
-  },
-  "name": "v1.0.0",
-  "message": null
-}
-```
-The message will be `nil` when creating a lightweight tag otherwise
-it will contain the annotation.
-
-It returns 200 if the operation succeed. In case of an error,
-405 with an explaining error message is returned.
-
 ## List repository tree
 
 Get a list of repository files and directories in a project.
diff --git a/doc/api/tags.md b/doc/api/tags.md
new file mode 100644
index 0000000000000000000000000000000000000000..085d387e824240c39f3aec0410c385dd4a80321d
--- /dev/null
+++ b/doc/api/tags.md
@@ -0,0 +1,131 @@
+# Tags
+
+## List project repository tags
+
+Get a list of repository tags from a project, sorted by name in reverse alphabetical order.
+
+```
+GET /projects/:id/repository/tags
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+
+```json
+[
+  {
+    "commit": {
+      "author_name": "John Smith",
+      "author_email": "john@example.com",
+      "authored_date": "2012-05-28T04:42:42-07:00",
+      "committed_date": "2012-05-28T04:42:42-07:00",
+      "committer_name": "Jack Smith",
+      "committer_email": "jack@example.com",
+      "id": "2695effb5807a22ff3d138d593fd856244e155e7",
+      "message": "Initial commit",
+      "parents_ids": [
+        "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
+      ]
+    },
+    "release": {
+      "tag_name": "1.0.0",
+      "description": "Amazing release. Wow"
+    },
+    "name": "v1.0.0",
+    "message": null
+  }
+]
+```
+
+## Create a new tag
+
+Creates a new tag in the repository that points to the supplied ref.
+
+```
+POST /projects/:id/repository/tags
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `tag_name` (required) - The name of a tag
+- `ref` (required) - Create tag using commit SHA, another tag name, or branch name.
+- `message` (optional) - Creates annotated tag.
+- `release_description` (optional) - Add release notes to the git tag and store it in the GitLab database.
+
+```json
+{
+  "commit": {
+    "author_name": "John Smith",
+    "author_email": "john@example.com",
+    "authored_date": "2012-05-28T04:42:42-07:00",
+    "committed_date": "2012-05-28T04:42:42-07:00",
+    "committer_name": "Jack Smith",
+    "committer_email": "jack@example.com",
+    "id": "2695effb5807a22ff3d138d593fd856244e155e7",
+    "message": "Initial commit",
+    "parents_ids": [
+      "2a4b78934375d7f53875269ffd4f45fd83a84ebe"
+    ]
+  },
+  "release": {
+    "tag_name": "1.0.0",
+    "description": "Amazing release. Wow"
+  },
+  "name": "v1.0.0",
+  "message": null
+}
+```
+The message will be `nil` when creating a lightweight tag otherwise
+it will contain the annotation.
+
+It returns 200 if the operation succeed. In case of an error,
+405 with an explaining error message is returned.
+
+
+## Create a new release
+
+Add release notes to the existing git tag. It returns 201 if the release is
+created successfully. If the tag does not exist, 404 is returned. If there
+already exists a release for the given tag, 409 is returned.
+
+```
+POST /projects/:id/repository/tags/:tag_name/release
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `tag_name` (required) - The name of a tag
+- `description` (required) - Release notes with markdown support
+
+```json
+{
+  "tag_name": "1.0.0",
+  "description": "Amazing release. Wow"
+}
+```
+
+## Update a release
+
+Updates the release notes of a given release. It returns 200 if the release is
+successfully updated. If the tag or the release does not exist, it returns 404
+with a proper error message.
+
+```
+PUT /projects/:id/repository/tags/:tag_name/release
+```
+
+Parameters:
+
+- `id` (required) - The ID of a project
+- `tag_name` (required) - The name of a tag
+- `description` (required) - Release notes with markdown support
+
+```json
+{
+  "tag_name": "1.0.0",
+  "description": "Amazing release. Wow"
+}
+```
\ No newline at end of file
diff --git a/doc/ci/docker/using_docker_build.md b/doc/ci/docker/using_docker_build.md
index 5af27470d2fc31106bfea08f0afb64fa1ad8e101..4b1788a9af0c89bda6aa331c3e95a1f01adde818 100644
--- a/doc/ci/docker/using_docker_build.md
+++ b/doc/ci/docker/using_docker_build.md
@@ -35,7 +35,7 @@ GitLab Runner then executes build scripts as `gitlab-runner` user.
 
     ```bash
     $ sudo gitlab-runner register -n \
-      --url http://gitlab.com/ci \
+      --url https://gitlab.com/ci \
       --token RUNNER_TOKEN \
       --executor shell
       --description "My Runner"
@@ -84,7 +84,7 @@ In order to do that follow the steps:
 
     ```bash
     $ sudo gitlab-runner register -n \
-      --url http://gitlab.com/ci \
+      --url https://gitlab.com/ci \
       --token RUNNER_TOKEN \
       --executor docker \
       --description "My Docker Runner" \
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index ef8a7ec1e8613c45d1002b68999b1b0350ae09fa..64e52eba3a2240574240a6351c8f3b6d930bf42b 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -60,11 +60,11 @@ This is image that have fully preconfigured `wordpress` and have `MySQL` server
 ```
 
 Next time when you run your application the `tutum/wordpress` will be started 
-and you will have access to it from your build container under hostname: `tutum_wordpress`.
+and you will have access to it from your build container under hostname: `tutum__wordpress`.
 
 Alias hostname for the service is made from the image name:
 1. Everything after `:` is stripped,
-2. '/' is replaced to `_`.
+2. '/' is replaced with `__`.
 
 ### Configuring services
 Many services accept environment variables, which allow you to easily change database names or set account names depending on the environment.
diff --git a/doc/ci/img/builds_tab.png b/doc/ci/img/builds_tab.png
new file mode 100644
index 0000000000000000000000000000000000000000..d088b8b329dc5dca75513e4fbe27d2323203d770
Binary files /dev/null and b/doc/ci/img/builds_tab.png differ
diff --git a/doc/ci/quick_start/README.md b/doc/ci/quick_start/README.md
index a87a1f806fc226adeb270fb7be27897414993d13..a9b36139de94c376fca4576388d59eb6c6e2d9b3 100644
--- a/doc/ci/quick_start/README.md
+++ b/doc/ci/quick_start/README.md
@@ -1,44 +1,62 @@
 # Quick Start
 
-To start building projects with GitLab CI a few steps needs to be done.
+Starting from version 8.0, GitLab Continuous Integration (CI) is fully
+integrated into GitLab itself and is enabled by default on all projects.
 
-## 1. Install GitLab and CI
+This guide assumes that you:
 
-First you need to have a working GitLab and GitLab CI instance.
+- have a working GitLab instance of version 8.0 or higher or are using
+  [GitLab.com](https://gitlab.com/users/sign_in)
+- have a project in GitLab that you would like to use CI for
 
-You can omit this step if you use [GitLab.com](http://GitLab.com/).
+In brief, the steps needed to have a working CI can be summed up to:
 
-## 2. Create repository on GitLab
+1. Create a new project
+1. Add `.gitlab-ci.yml` to the git repository and push to GitLab
+1. Configure a Runner
 
-Once you login on your GitLab add a new repository where you will store your source code.
-Push your application to that repository.
+From there on, on every push to your git repository the build will be
+automagically started by the Runner and will appear under the project's
+`/builds` page.
 
-## 3. Add project to CI
+Now, let's break it down to pieces and work on solving the GitLab CI puzzle.
 
-The next part is to login to GitLab CI.
-Point your browser to the URL you have set GitLab or use [gitlab.com/ci](http://gitlab.com/ci/).
+## Creating a `.gitlab-ci.yml` file
 
-On the first screen you will see a list of GitLab's projects that you have access to:
+Before you create `.gitlab-ci.yml` let's first explain in brief what this is
+all about.
 
-![Projects](projects.png)
+### What is `.gitlab-ci.yml`
 
-Click **Add Project to CI**.
-This will create project in CI and authorize GitLab CI to fetch sources from GitLab.
+The `.gitlab-ci.yml` file is where you configure what CI does with your project.
+It lives in the root of your repository.
 
-> GitLab CI creates unique token that is used to configure GitLab CI service in GitLab.
-> This token allows to access GitLab's repository and configures GitLab to trigger GitLab CI webhook on **Push events** and **Tag push events**.
-> You can see that token by going to Project's Settings > Services > GitLab CI.
-> You will see there token, the same token is assigned in GitLab CI settings of project.
+On any push to your repository, GitLab will look for the `.gitlab-ci.yml`
+file and start builds on _Runners_ according to the contents of the file,
+for that commit.
 
-## 4. Create project's configuration - .gitlab-ci.yml
+Because `.gitlab-ci.yml` is in the repository, it is version controlled,
+old versions still build succesfully, forks can easily make use of CI,
+branches can have separate builds and you have a single source of truth for CI.
+You can read more about the reasons why we are using `.gitlab-ci.yml`
+[in our blog about it][blog-ci].
 
-The next: You have to define how your project will be built.
-GitLab CI uses [YAML](https://en.wikipedia.org/wiki/YAML) file to store build configuration.
-You need to create `.gitlab-ci.yml` in root directory of your repository:
+**Note:** `.gitlab-ci.yml` is a [YAML](https://en.wikipedia.org/wiki/YAML) file
+so you have to pay extra attention to the identation. Always use spaces, not
+tabs.
+
+### Creating a simple `.gitlab-ci.yml` file
+
+You need to create a file named `.gitlab-ci.yml` in the root directory of your
+repository. Below is an example for a Ruby on Rails project.
 
 ```yaml
 before_script:
-  - bundle install
+  - apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
+  - ruby -v
+  - which ruby
+  - gem install bundler --no-ri --no-rdoc
+  - bundle install --jobs $(nproc)  "${FLAGS[@]}"
 
 rspec:
   script:
@@ -49,71 +67,131 @@ rubocop:
     - bundle exec rubocop
 ```
 
-This is the simplest possible build configuration that will work for most Ruby applications:
-1. Define two jobs `rspec` and `rubocop` with two different commands to be executed.
-1. Before every job execute commands defined by `before_script`.
+This is the simplest possible build configuration that will work for most Ruby
+applications:
+
+1. Define two jobs `rspec` and `rubocop` (the names are arbitrary) with
+   different commands to be executed.
+1. Before every job, the commands defined by `before_script` are executed.
 
-The `.gitlab-ci.yml` defines set of jobs with constrains how and when they should be run.
-The jobs are defined as top-level elements with name and always have to contain the `script`.
-Jobs are used to create builds, which are then picked by [runners](../runners/README.md) and executed within environment of the runner.
-What is important that each job is run independently from each other. 
+The `.gitlab-ci.yml` file defines sets of jobs with constraints of how and when
+they should be run. The jobs are defined as top-level elements with a name (in
+our case `rspec` and `rubocop`) and always have to contain the `script` keyword.
+Jobs are used to create builds, which are then picked by
+[Runners](../runners/README.md) and executed within the environment of the Runner.
 
-For more information and complete `.gitlab-ci.yml` syntax, please check the [Configuring project (.gitlab-ci.yml)](../yaml/README.md).
+What is important is that each job is run independently from each other.
 
-## 5. Add file and push .gitlab-ci.yml to repository
+If you want to check whether your `.gitlab-ci.yml` file is valid, there is a
+Lint tool under the page `/ci/lint` of your GitLab instance. You can also find
+the link under **Settings > CI settings** in your project.
 
-Once you created `.gitlab-ci.yml` you should add it to git repository and push it to GitLab.
+For more information and a complete `.gitlab-ci.yml` syntax, please check
+[the documentation on .gitlab-ci.yml](../yaml/README.md).
+
+### Push `.gitlab-ci.yml` to GitLab
+
+Once you've created `.gitlab-ci.yml`, you should add it to your git repository
+and push it to GitLab.
 
 ```bash
 git add .gitlab-ci.yml
-git commit
+git commit -m "Add .gitlab-ci.yml"
 git push origin master
 ```
 
-If you refresh the project's page on GitLab CI you will notice a one new commit:
+Now if you go to the **Builds** page you will see that the builds are pending.
+
+You can also go to the **Commits** page and notice the little clock icon next
+to the commit SHA.
+
+![New commit pending](img/new_commit.png)
+
+Clicking on the clock icon you will be directed to the builds page for that
+specific commit.
+
+![Single commit builds page](img/single_commit_status_pending.png)
+
+Notice that there are two jobs pending which are named after what we wrote in
+`.gitlab-ci.yml`. The red triangle indicates that there is no Runner configured
+yet for these builds.
+
+The next step is to configure a Runner so that it picks the pending jobs.
+
+## Configuring a Runner
+
+In GitLab, Runners run the builds that you define in `.gitlab-ci.yml`.
+A Runner can be a virtual machine, a VPS, a bare-metal machine, a docker
+container or even a cluster of containers. GitLab and the Runners communicate
+through an API, so the only needed requirement is that the machine on which the
+Runner is configured to has Internet access.
+
+A Runner can be specific to a certain project or serve multiple projects in
+GitLab. If it serves all projects it's called a _Shared Runner_.
+
+Find more information about different Runners in the
+[Runners](../runners/README.md) documentation.
+
+You can find whether any Runners are assigned to your project by going to
+**Settings > Runners**. Setting up a Runner is easy and straightforward. The
+official Runner supported by GitLab is written in Go and can be found at
+<https://gitlab.com/gitlab-org/gitlab-ci-multi-runner>.
+
+In order to have a functional Runner you need to follow two steps:
+
+1. [Install it][runner-install]
+2. [Configure it](../runners/README.md#registering-a-specific-runner)
+
+Follow the links above to set up your own Runner or use a Shared Runner as
+described in the next section.
+
+For other types of unofficial Runners written in other languages, see the
+[instructions for the various GitLab Runners](https://about.gitlab.com/gitlab-ci/#gitlab-runner).
+
+Once the Runner has been set up, you should see it on the Runners page of your
+project, following **Settings > Runners**.
 
-![](new_commit.png)
+![Activated runners](img/runners_activated.png)
 
-However the commit has status **pending** which means that commit was not yet picked by runner.
+### Shared Runners
 
-## 6. Configure runner
+If you use [GitLab.com](https://gitlab.com/) you can use **Shared Runners**
+provided by GitLab Inc.
 
-In GitLab CI, Runners run your builds.
-A runner is a machine (can be virtual, bare-metal or VPS) that picks up builds through the coordinator API of GitLab CI.
+These are special virtual machines that run on GitLab's infrastructure and can
+build any project.
 
-A runner can be specific to a certain project or serve any project in GitLab CI.
-A runner that serves all projects is called a shared runner.
-More information about different runner types can be found in [Configuring runner](../runners/README.md).
+To enable **Shared Runners** you have to go to your project's
+**Settings > Runners** and click **Enable shared runners**.
 
-To check if you have runners assigned to your project go to **Runners**. You will find there information how to setup project specific runner:
+[Read more on Shared Runners](../runners/README.md).
 
-1. Install GitLab Runner software. Checkout the [GitLab Runner](https://about.gitlab.com/gitlab-ci/#gitlab-runner) section to install it.
-1. Specify following URL during runner setup: https://gitlab.com/ci/
-1. Use the following registration token during setup: TOKEN
+## Seeing the status of your build
 
-If you do it correctly your runner should be shown under **Runners activated for this project**:
+After configuring the Runner succesfully, you should see the status of your
+last commit change from _pending_ to either _running_, _success_ or _failed_.
 
-![](runners_activated.png)
+You can view all builds, by going to the **Builds** page in your project.
 
-### Shared runners
+![Commit status](img/builds_status.png)
 
-If you use [gitlab.com/ci](http://gitlab.com/ci/) you can use **Shared runners** provided by GitLab Inc.
-These are special virtual machines that are run on GitLab's infrastructure that can build any project.
-To enable **Shared runners** you have to go to **Runners** and click **Enable shared runners** for this project.
+By clicking on a Build ID, you will be able to see the log of that build.
+This is important to diagnose why a build failed or acted differently than
+you expected.
 
-## 7. Check status of commit
+![Build log](img/build_log.png)
 
-If everything went OK and you go to commit, the status of the commit should change from **pending** to either **running**, **success** or **failed**.
+You are also able to view the status of any commit in the various pages in
+GitLab, such as **Commits** and **Merge Requests**.
 
-![](commit_status.png)
+## Next steps
 
-You can click **Build ID** to view build log for specific job.
+Awesome! You started using CI in GitLab!
 
-## 8. Congratulations!
+Next you can look into doing more with the CI. Many people are using GitLab
+to package, containerize, test and deploy software.
 
-You managed to build your first project using GitLab CI.
-You may need to tune your `.gitlab-ci.yml` file to implement build plan for your project.
-A few examples how it can be done you can find on [Examples](../examples/README.md) page.
+Visit our various languages examples at <https://gitlab.com/groups/gitlab-examples>.
 
-GitLab CI also offers **the Lint** tool to verify validity of your `.gitlab-ci.yml` which can be useful to troubleshoot potential problems.
-The Lint is available from project's settings or by adding `/lint` to GitLab CI url.
+[runner-install]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner/tree/master#installation
+[blog-ci]: https://about.gitlab.com/2015/05/06/why-were-replacing-gitlab-ci-jobs-with-gitlab-ci-dot-yml/
diff --git a/doc/ci/quick_start/build_status.png b/doc/ci/quick_start/build_status.png
deleted file mode 100644
index 333259e6acd301b579ebb7b7682b1df1e635a61a..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/build_status.png and /dev/null differ
diff --git a/doc/ci/quick_start/commit_status.png b/doc/ci/quick_start/commit_status.png
deleted file mode 100644
index 725b79e6f91d1de8e8240c28f911b2753d92222a..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/commit_status.png and /dev/null differ
diff --git a/doc/ci/quick_start/img/build_log.png b/doc/ci/quick_start/img/build_log.png
new file mode 100644
index 0000000000000000000000000000000000000000..89e6cd40cb61d890b7ee1ce0137c738a18526e74
Binary files /dev/null and b/doc/ci/quick_start/img/build_log.png differ
diff --git a/doc/ci/quick_start/img/builds_status.png b/doc/ci/quick_start/img/builds_status.png
new file mode 100644
index 0000000000000000000000000000000000000000..b8e6c2a361a093059f2ab189a2e281a22175ad7a
Binary files /dev/null and b/doc/ci/quick_start/img/builds_status.png differ
diff --git a/doc/ci/quick_start/img/new_commit.png b/doc/ci/quick_start/img/new_commit.png
new file mode 100644
index 0000000000000000000000000000000000000000..3d3c9d5c0bd078333a7bc1e670b9a7dc736a2fcc
Binary files /dev/null and b/doc/ci/quick_start/img/new_commit.png differ
diff --git a/doc/ci/quick_start/img/runners_activated.png b/doc/ci/quick_start/img/runners_activated.png
new file mode 100644
index 0000000000000000000000000000000000000000..eafcfd6ecd5daa3ebdea96496b2126f756ace99e
Binary files /dev/null and b/doc/ci/quick_start/img/runners_activated.png differ
diff --git a/doc/ci/quick_start/img/single_commit_status_pending.png b/doc/ci/quick_start/img/single_commit_status_pending.png
new file mode 100644
index 0000000000000000000000000000000000000000..23b3bb5acfcfd4d44f76152f92fab81d95d7b500
Binary files /dev/null and b/doc/ci/quick_start/img/single_commit_status_pending.png differ
diff --git a/doc/ci/quick_start/img/status_pending.png b/doc/ci/quick_start/img/status_pending.png
new file mode 100644
index 0000000000000000000000000000000000000000..a049ec2a5ba560c222078da1c45b3e358ce15dd3
Binary files /dev/null and b/doc/ci/quick_start/img/status_pending.png differ
diff --git a/doc/ci/quick_start/new_commit.png b/doc/ci/quick_start/new_commit.png
deleted file mode 100644
index 3839e893c177ee81865b6791d2b81b664a4dcfc0..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/new_commit.png and /dev/null differ
diff --git a/doc/ci/quick_start/projects.png b/doc/ci/quick_start/projects.png
deleted file mode 100644
index 0b3430a69dbfafbe71d780c13025226499e82407..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/projects.png and /dev/null differ
diff --git a/doc/ci/quick_start/runners.png b/doc/ci/quick_start/runners.png
deleted file mode 100644
index 25b4046bc00a8843d2dc68125ce8a9864e541df3..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/runners.png and /dev/null differ
diff --git a/doc/ci/quick_start/runners_activated.png b/doc/ci/quick_start/runners_activated.png
deleted file mode 100644
index c934bd12f41dc17d10eb131b233f630b18fc1761..0000000000000000000000000000000000000000
Binary files a/doc/ci/quick_start/runners_activated.png and /dev/null differ
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index 5d35d1da4ee9496952d8bf19e0dbe193f1f00fb3..3dbf1afc7a96ca8e7b2730be1e3e98f8620d5f92 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -56,6 +56,7 @@ There are a few `keywords` that can't be used as job names:
 | types         | optional | Alias for `stages` |
 | before_script | optional | Define commands prepended for each job's script |
 | variables     | optional | Define build variables |
+| cache         | optional | Define list of files that should be cached between subsequent runs |
 
 ### image and services
 This allows to specify a custom Docker image and a list of services that can be used for time of the build.
@@ -110,6 +111,19 @@ These variables can be later used in all executed commands and scripts.
 
 The YAML-defined variables are also set to all created service containers, thus allowing to fine tune them.
 
+### cache
+`cache` is used to specify list of files and directories which should be cached between builds.
+
+**The global setting allows to specify default cached files for all jobs.**
+
+To cache all git untracked files and files in `binaries`:
+```
+cache:
+  untracked: true
+  paths:
+  - binaries/
+```
+
 ## Jobs
 `.gitlab-ci.yml` allows you to specify an unlimited number of jobs.
 Each job has to have a unique `job_name`, which is not one of the keywords mentioned above.
@@ -142,6 +156,7 @@ job_name:
 | allow_failure | optional | Allow build to fail. Failed build doesn't contribute to commit status |
 | when          | optional | Define when to run build. Can be `on_success`, `on_failure` or `always` |
 | artifacts     | optional | Define list build artifacts |
+| cache         | optional | Define list of files that should be cached between subsequent runs |
 
 ### script
 `script` is a shell script which is executed by runner. The shell script is prepended with `before_script`.
@@ -263,31 +278,74 @@ The above script will:
 `artifacts` is used to specify list of files and directories which should be attached to build after success.
 
 1. Send all files in `binaries` and `.config`:
-```
-artifacts:
-  paths:
-  - binaries/
-  - .config
-```
+
+        artifacts:
+          paths:
+          - binaries/
+          - .config
 
 2. Send all git untracked files:
-```
-artifacts:
-  untracked: true
-```
+
+        artifacts:
+          untracked: true
 
 3. Send all git untracked files and files in `binaries`:
-```
-artifacts:
-  untracked: true
-  paths:
-  - binaries/
-```
+
+        artifacts:
+          untracked: true
+          paths:
+          - binaries/
 
 The artifacts will be send after the build success to GitLab and will be accessible in GitLab interface to download.
 
 This feature requires GitLab Runner v0.7.0 or higher.
 
+### cache
+`cache` is used to specify list of files and directories which should be cached between builds.
+
+1. Cache all files in `binaries` and `.config`:
+
+        rspec:
+          script: test
+          cache:
+            paths:
+            - binaries/
+            - .config
+
+2. Cache all git untracked files:
+
+        rspec:
+          script: test
+          cache:
+            untracked: true
+            
+3. Cache all git untracked files and files in `binaries`:
+
+        rspec:
+          script: test
+          cache:
+            untracked: true
+            paths:
+            - binaries/
+
+4. Locally defined cache overwrites globally defined options. This will cache only `binaries/`:
+
+        cache:
+          paths:
+          - my/files
+        
+        rspec:
+          script: test
+          cache:
+            paths:
+            - binaries/
+
+The cache is provided on best effort basis, so don't expect that cache will be present.
+For implementation details please check GitLab Runner.
+
+This feature requires GitLab Runner v0.7.0 or higher.
+
+
 ## Validate the .gitlab-ci.yml
 Each instance of GitLab CI has an embedded debug tool called Lint.
 You can find the link to the Lint in the project's settings page or use short url `/lint`.
diff --git a/doc/customization/libravatar.md b/doc/customization/libravatar.md
index 54c1780c3abeef85a906e93dfbcb063de2f34ce4..bd2c242afc2bf031fdbb7a0d779f2bf8ef13f818 100644
--- a/doc/customization/libravatar.md
+++ b/doc/customization/libravatar.md
@@ -2,7 +2,7 @@
 
 GitLab by default supports [Gravatar](https://gravatar.com) avatar service.
 Libravatar is a service which delivers your avatar (profile picture) to other websites and their API is
-[heavily based on gravatar](http://wiki.libravatar.org/api/).
+[heavily based on gravatar](https://wiki.libravatar.org/api/).
 
 This means that it is not complicated to switch to Libravatar avatar service or even self hosted Libravatar server.
 
@@ -31,7 +31,7 @@ the configuration options as follows:
 
 ## Self-hosted
 
-If you are [running your own libravatar service](http://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration
+If you are [running your own libravatar service](https://wiki.libravatar.org/running_your_own/) the URL will be different in the configuration
 but the important part is to provide the same placeholders so GitLab can parse the URL correctly.
 
 For example, you host a service on `http://libravatar.example.com` the `plain_url` you need to supply in `gitlab.yml` is
@@ -63,7 +63,7 @@ Run `sudo gitlab-ctl reconfigure` for changes to take effect.
 
 ## Default URL for missing images
 
-[Libravatar supports different sets](http://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
+[Libravatar supports different sets](https://wiki.libravatar.org/api/) of `missing images` for emails not found on the Libravatar service.
 
 In order to use a different set other than `identicon`, replace `&d=identicon` portion of the URL with another supported set.
 For example, you can use `retro` set in which case the URL would look like: `plain_url: "http://cdn.libravatar.org/avatar/%{hash}?s=%{size}&d=retro"`
diff --git a/doc/development/architecture.md b/doc/development/architecture.md
index c00d290371e032e45beea68e51236ea00f54db55..6101a71a8dea813add6189ae68b90a8c74fcfa7e 100644
--- a/doc/development/architecture.md
+++ b/doc/development/architecture.md
@@ -146,7 +146,7 @@ nginx
 
 Apache httpd
 
-- [Explanation of Apache logs](http://httpd.apache.org/docs/2.2/logs.html).
+- [Explanation of Apache logs](https://httpd.apache.org/docs/2.2/logs.html).
 - `/var/log/apache2/` contains error and output logs (on Ubuntu).
 - `/var/log/httpd/` contains error and output logs (on RHEL).
 
diff --git a/doc/hooks/custom_hooks.md b/doc/hooks/custom_hooks.md
index 548c484bc0842e7edde8ac0750e971a140bec21e..0f2665a3bf7162a4cf4b216296a9f6e6041f676e 100644
--- a/doc/hooks/custom_hooks.md
+++ b/doc/hooks/custom_hooks.md
@@ -7,7 +7,7 @@ Please explore webhooks as an option if you do not have filesystem access. For a
 Git natively supports hooks that are executed on different actions.
 Examples of server-side git hooks include pre-receive, post-receive, and update.
 See
-[Git SCM Server-Side Hooks](http://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks)
+[Git SCM Server-Side Hooks](https://git-scm.com/book/en/v2/Customizing-Git-Git-Hooks#Server-Side-Hooks)
 for more information about each hook type.
 
 As of gitlab-shell version 2.2.0 (which requires GitLab 7.5+), GitLab
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index c565e90da2f505a140cd90787c5690229087a18c..513ad69ec26d9f07ae5656e2d0955c632fc48622 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -2,7 +2,7 @@
 
 ## Note
 
-We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](http://bugs.mysql.com/bug.php?id=65830) that [suggested](http://bugs.mysql.com/bug.php?id=50909) [fixes](http://bugs.mysql.com/bug.php?id=65830) [have](http://bugs.mysql.com/bug.php?id=63164).
+We do not recommend using MySQL due to various issues. For example, case [(in)sensitivity](https://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html) and [problems](https://bugs.mysql.com/bug.php?id=65830) that [suggested](https://bugs.mysql.com/bug.php?id=50909) [fixes](https://bugs.mysql.com/bug.php?id=65830) [have](https://bugs.mysql.com/bug.php?id=63164).
 
 ## MySQL
 
diff --git a/doc/install/installation.md b/doc/install/installation.md
index 8028e51dbcdea99e929268b40b8b33b7eb5cbd5e..618391e16d224b3d5b5ad66a01beda0f72b8d318 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -106,7 +106,7 @@ Then select 'Internet Site' and press enter to confirm the hostname.
 
 ## 2. Ruby
 
-The use of Ruby version managers such as [RVM](http://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby.
+The use of Ruby version managers such as [RVM](https://rvm.io/), [rbenv](https://github.com/sstephenson/rbenv) or [chruby](https://github.com/postmodern/chruby) with GitLab in production frequently leads to hard to diagnose problems. For example, GitLab Shell is called from OpenSSH and having a version manager can prevent pushing and pulling over SSH. Version managers are not supported and we strongly advise everyone to follow the instructions below to use a system Ruby.
 
 Remove the old Ruby 1.8 if present
 
@@ -128,11 +128,10 @@ Install the Bundler Gem:
 
 ## 3. Go
 
-Since GitLab 8.0, Git HTTP requests are handled by gitlab-git-http-server.
-This is a small daemon written in Go.
-To install gitlab-git-http-server we need a Go compiler.
-The instructions below assume you use 64-bit Linux. You can find
-downloads for other platforms at the [Go download
+Since GitLab 8.0, Git HTTP requests are handled by gitlab-workhorse (formerly
+gitlab-git-http-server). This is a small daemon written in Go. To install
+gitlab-workhorse we need a Go compiler. The instructions below assume you
+use 64-bit Linux. You can find downloads for other platforms at the [Go download
 page](https://golang.org/dl).
 
     curl -O --progress https://storage.googleapis.com/golang/go1.5.1.linux-amd64.tar.gz
@@ -211,9 +210,9 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 ### Clone the Source
 
     # Clone GitLab repository
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-1-stable gitlab
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-2-stable gitlab
 
-**Note:** You can change `8-1-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-2-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -298,7 +297,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 
 ### Install Gems
 
-**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](http://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
+**Note:** As of bundler 1.5.2, you can invoke `bundle install -jN` (where `N` the number of your processor cores) and enjoy the parallel gems installation with measurable difference in completion time (~60% faster). Check the number of your cores with `nproc`. For more information check this [post](https://robots.thoughtbot.com/parallel-gem-installing-using-bundler). First make sure you have bundler >= 1.5.2 (run `bundle -v`) as it addresses some [issues](https://devcenter.heroku.com/changelog-items/411) that were [fixed](https://github.com/bundler/bundler/pull/2817) in 1.5.2.
 
     # For PostgreSQL (note, the option says "without ... mysql")
     sudo -u git -H bundle install --deployment --without development test mysql aws kerberos
@@ -313,7 +312,7 @@ We recommend using a PostgreSQL database. For MySQL check [MySQL setup guide](da
 GitLab Shell is an SSH access and repository management software developed specially for GitLab.
 
     # Run the installation task for gitlab-shell (replace `REDIS_URL` if needed):
-    sudo -u git -H bundle exec rake gitlab:shell:install[v2.6.6] REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
+    sudo -u git -H bundle exec rake gitlab:shell:install REDIS_URL=unix:/var/run/redis/redis.sock RAILS_ENV=production
 
     # By default, the gitlab-shell config is generated from your main GitLab config.
     # You can review (and modify) the gitlab-shell config as follows:
@@ -321,19 +320,19 @@ GitLab Shell is an SSH access and repository management software developed speci
 
 **Note:** If you want to use HTTPS, see [Using HTTPS](#using-https) for the additional steps.
 
-**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1  hostname"). This might be necessary for example if you set up gitlab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)".
+**Note:** Make sure your hostname can be resolved on the machine itself by either a proper DNS record or an additional line in /etc/hosts ("127.0.0.1  hostname"). This might be necessary for example if you set up GitLab behind a reverse proxy. If the hostname cannot be resolved, the final installation check will fail with "Check GitLab API access: FAILED. code: 401" and pushing commits will be rejected with "[remote rejected] master -> master (hook declined)".
 
-### Install gitlab-git-http-server
+### Install gitlab-workhorse
 
     cd /home/git
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-git-http-server.git
-    cd gitlab-git-http-server
-    sudo -u git -H git checkout 0.3.0
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
+    cd gitlab-workhorse
+    sudo -u git -H git checkout 0.4.2
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
-    
-    # Go to Gitlab installation folder
+
+    # Go to GitLab installation folder
 
     cd /home/git/gitlab
 
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 9b7d8fa3969694931a22505468d1ea89d8f546d2..7e2920b8865e6aeb7d4dfed2af12cb67640e5360 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -71,7 +71,7 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
 
   # Filter LDAP users
   #
-  #   Format: RFC 4515 http://tools.ietf.org/search/rfc4515
+  #   Format: RFC 4515 https://tools.ietf.org/search/rfc4515
   #   Ex. (employeeType=developer)
   #
   #   Note: GitLab does not support omniauth-ldap's custom filter syntax.
@@ -145,7 +145,7 @@ If multiple LDAP email attributes are present, e.g. `mail: foo@bar.com` and `ema
 ## Using an LDAP filter to limit access to your GitLab server
 
 If you want to limit all GitLab access to a subset of the LDAP users on your LDAP server you can set up an LDAP user filter.
-The filter must comply with [RFC 4515](http://tools.ietf.org/search/rfc4515).
+The filter must comply with [RFC 4515](https://tools.ietf.org/search/rfc4515).
 
 ```ruby
 # For omnibus packages; new LDAP server syntax
diff --git a/doc/integration/omniauth.md b/doc/integration/omniauth.md
index bd9550c6ddb12cdefc688f9471763d4faf0862ce..f2b1721fc0304f38007df293d67c363d04702ed4 100644
--- a/doc/integration/omniauth.md
+++ b/doc/integration/omniauth.md
@@ -36,7 +36,7 @@ If you want to change these settings:
     ```
     gitlab_rails['omniauth_enabled'] = true
     gitlab_rails['omniauth_allow_single_sign_on'] = false
-    gitlab_rails['block_auto_created_users'] = true
+    gitlab_rails['omniauth_block_auto_created_users'] = true
     ```
 
 * **For installations from source**
diff --git a/doc/legal/corporate_contributor_license_agreement.md b/doc/legal/corporate_contributor_license_agreement.md
index 13bf15fcf45c0c7b260d022aa0f1f6d6b02e2480..7b94506c29785b55d27a9e2c1ea40fa3ed3779ae 100644
--- a/doc/legal/corporate_contributor_license_agreement.md
+++ b/doc/legal/corporate_contributor_license_agreement.md
@@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and
 
 8.  It is your responsibility to notify GitLab B.V. when any change is required to the list of designated employees authorized to submit Contributions on behalf of the Corporation, or to the Corporation's Point of Contact with GitLab B.V..
 
-This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
+This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/legal/individual_contributor_license_agreement.md b/doc/legal/individual_contributor_license_agreement.md
index 72b01433dd012433f46966ebc679f551283daafd..f97c252fd7cfa11bb270ee6b4ce0a3994e11b8f1 100644
--- a/doc/legal/individual_contributor_license_agreement.md
+++ b/doc/legal/individual_contributor_license_agreement.md
@@ -22,4 +22,4 @@ You accept and agree to the following terms and conditions for Your present and
 
 8.  You agree to notify GitLab B.V. of any facts or circumstances of which you become aware that would make these representations inaccurate in any respect.
 
-This text is licensed under the [Creative Commons Attribution 3.0 License](http://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
+This text is licensed under the [Creative Commons Attribution 3.0 License](https://creativecommons.org/licenses/by/3.0/) and the original source is the Google Open Source Programs Office.
diff --git a/doc/markdown/markdown.md b/doc/markdown/markdown.md
index ac3851f8c95fa9a1acc8ea95fa244382422a6844..bc8e7d155e7f761ff296f8617fd1b5e1457e4ec1 100644
--- a/doc/markdown/markdown.md
+++ b/doc/markdown/markdown.md
@@ -43,7 +43,7 @@ You can also use other rich text files in GitLab. You might have to install a de
 
 ## Newlines
 
-GFM honors the markdown specification in how [paragraphs and line breaks are handled](http://daringfireball.net/projects/markdown/syntax#p).
+GFM honors the markdown specification in how [paragraphs and line breaks are handled](https://daringfireball.net/projects/markdown/syntax#p).
 
 A paragraph is simply one or more consecutive lines of text, separated by one or more blank lines.  
 Line-breaks, or softreturns, are rendered if you end a line with two or more spaces
@@ -72,14 +72,14 @@ do_this_and_do_that_and_another_thing
 
 GFM will autolink almost any URL you copy and paste into your text.
 
-    * http://www.google.com
+    * https://www.google.com
     * https://google.com/
     * ftp://ftp.us.debian.org/debian/
     * smb://foo/bar/baz
     * irc://irc.freenode.net/gitlab
     * http://localhost:3000
 
-* http://www.google.com
+* https://www.google.com
 * https://google.com/
 * ftp://ftp.us.debian.org/debian/
 * smb://foo/bar/baz
@@ -390,7 +390,7 @@ There are two ways to create links, inline-style and reference-style.
 
     [arbitrary case-insensitive reference text]: https://www.mozilla.org
     [1]: http://slashdot.org
-    [link text itself]: http://www.reddit.com
+    [link text itself]: https://www.reddit.com
 
 [I'm an inline-style link](https://www.google.com)
 
@@ -406,7 +406,7 @@ Some text to show that the reference links can follow later.
 
 [arbitrary case-insensitive reference text]: https://www.mozilla.org
 [1]: http://slashdot.org
-[link text itself]: http://www.reddit.com
+[link text itself]: https://www.reddit.com
 
 **Note**
 
@@ -583,5 +583,5 @@ By including colons in the header row, you can align the text within that column
 ## References
 
 - This document leveraged heavily from the [Markdown-Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet).
-- The [Markdown Syntax Guide](http://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
+- The [Markdown Syntax Guide](https://daringfireball.net/projects/markdown/syntax) at Daring Fireball is an excellent resource for a detailed explanation of standard markdown.
 - [Dillinger.io](http://dillinger.io) is a handy tool for testing standard markdown.
diff --git a/doc/operations/unicorn.md b/doc/operations/unicorn.md
index 3998da01f015ea00437ebbbbecb5192127e20c50..bad61151bda1ff45f7fb7a57a7f18026047f7b83 100644
--- a/doc/operations/unicorn.md
+++ b/doc/operations/unicorn.md
@@ -52,7 +52,7 @@ leak memory, probably because it does not handle user requests.)
 
 To make these memory leaks manageable, GitLab comes with the
 [unicorn-worker-killer gem](https://github.com/kzk/unicorn-worker-killer). This
-gem [monkey-patches](http://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
+gem [monkey-patches](https://en.wikipedia.org/wiki/Monkey_patch) the Unicorn
 workers to do a memory self-check after every 16 requests. If the memory of the
 Unicorn worker exceeds a pre-set limit then the worker process exits. The
 Unicorn master then automatically replaces the worker process.
@@ -83,4 +83,4 @@ is a normal value for our current GitLab.com setup and traffic.
 
 The high frequency of Unicorn memory restarts on some GitLab sites can be a
 source of confusion for administrators. Usually they are a [red
-herring](http://en.wikipedia.org/wiki/Red_herring).
+herring](https://en.wikipedia.org/wiki/Red_herring).
diff --git a/doc/permissions/permissions.md b/doc/permissions/permissions.md
index 8d4c2ceab7d25c4a25623010d87ab5487bff40b1..bcd00cfc6bf82f44c19fdc96980a6c19377bbfa7 100644
--- a/doc/permissions/permissions.md
+++ b/doc/permissions/permissions.md
@@ -6,6 +6,9 @@ If a user is both in a project group and in the project itself, the highest perm
 
 If a user is a GitLab administrator they receive all permissions.
 
+On public projects the Guest role is not enforced.  
+All users will be able to create issues, leave comments, and pull or download the project code.  
+
 To add or import a user, you can follow the [project users and members
 documentation](doc/workflow/add-user/add-user.md).
 
@@ -15,8 +18,8 @@ documentation](doc/workflow/add-user/add-user.md).
 |---------------------------------------|---------|------------|-------------|----------|--------|
 | Create new issue                      | 鉁�       | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
 | Leave comments                        | 鉁�       | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
-| Pull project code                     | 鉁�        | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
-| Download project                      | 鉁�        | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
+| Pull project code                     |         | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
+| Download project                      |         | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
 | Create code snippets                  |         | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
 | Manage issue tracker                  |         | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
 | Manage labels                         |         | 鉁�          | 鉁�           | 鉁�        | 鉁�      |
diff --git a/doc/public_access/public_access.md b/doc/public_access/public_access.md
index bd439f7c6f356856be11fff0e92aa028ed183b13..6e22ea7b72af8780ea6fba1fdb5a46213f8c584a 100644
--- a/doc/public_access/public_access.md
+++ b/doc/public_access/public_access.md
@@ -1,44 +1,59 @@
 # Public access
 
-GitLab allows you to open selected projects to be accessed **publicly** or **internally**.
+GitLab allows you to change your projects' visibility in order be accessed
+**publicly** or **internally**.
 
-Projects with either of these visibility levels will be listed in the [public access directory](/public).
+Projects with either of these visibility levels will be listed in the
+public access directory (`/public` under your GitLab instance).
+Here is the [GitLab.com example](https://gitlab.com/public).
 
 Internal projects will only be available to authenticated users.
 
-## Public projects
+## Visibility of projects
+
+### Public projects
 
 Public projects can be cloned **without any** authentication.
 
-It will also be listed on the [public access directory](/public).
+They will also be listed on the public access directory (`/public`).
 
-**Any logged in user** will have [Guest](../permissions/permissions) permissions on the repository.
+**Any logged in user** will have [Guest](../permissions/permissions)
+permissions on the repository.
 
-## Internal projects
+### Internal projects
 
 Internal projects can be cloned by any logged in user.
 
-It will also be listed on the [public access directory](/public) for logged in users.
+They will also be listed on the public access directory (`/public`) for logged
+in users.
 
-Any logged in user will have [Guest](../permissions/permissions) permissions on the repository.
+Any logged in user will have [Guest](../permissions/permissions) permissions on
+the repository.
 
-## How to change project visibility
+### How to change project visibility
 
-1. Go to your project dashboard
-1. Click on the "Edit" tab
-1. Change "Visibility Level"
+1. Go to your project's **Settings**
+1. Change "Visibility Level" to either Public, Internal or Private
 
 ## Visibility of users
 
-The public page of users, located at `/u/username` is visible if either:
+The public page of a user, located at `/u/username`, is always visible whether
+you are logged in or not.
+
+When visiting the public page of a user, you can only see the projects which
+you are privileged to.
 
-- You are logged in.
-- You are logged out, and the target user is authorized to (is Guest, Reporter, etc.) at least one public project.
+## Visibility of groups
 
-Otherwise, you will be redirected to the sign in page.
+The public page of a group, located at `/groups/groupname`, is always visible
+to everyone.
 
-When visiting the public page of an user, you will only see listed projects which you can view yourself.
+Logged out users will be able to see the description and the avatar of the
+group as well as all public projects belonging to that group.
 
 ## Restricting the use of public or internal projects
 
-In the Admin area under Settings you can disable public projects or public and internal projects for the entire GitLab installation to prevent people making code public by accident. The restricted visibility settings do not apply to admin users.
+In the Admin area under **Settings** (`/admin/application_settings`), you can
+restrict the use of visibility levels for users when they create a project or a
+snippet. This is useful to prevent people exposing their repositories to public
+by accident. The restricted visibility settings do not apply to admin users.
diff --git a/doc/raketasks/backup_restore.md b/doc/raketasks/backup_restore.md
index 1a5442cdac71838425065f70724fd469b7020010..b4d2786bd761666b77876af29d2e61073a53f996 100644
--- a/doc/raketasks/backup_restore.md
+++ b/doc/raketasks/backup_restore.md
@@ -29,7 +29,7 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
 ```
 
 Also you can choose what should be backed up by adding environment variable SKIP. Available options: db,
-uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts).
+uploads (attachments), repositories, builds(CI build output logs), artifacts (CI build artifacts), lfs (LFS objects).
 Use a comma to specify several options at the same time.
 
 ```
@@ -274,9 +274,6 @@ sudo gitlab-rake gitlab:backup:restore BACKUP=1393513186
 # Start GitLab
 sudo gitlab-ctl start
 
-# Create satellites
-sudo gitlab-rake gitlab:satellites:create
-
 # Check GitLab
 sudo gitlab-rake gitlab:check SANITIZE=true
 ```
diff --git a/doc/release/monthly.md b/doc/release/monthly.md
index 4925816daaa7be296aca4ab001893b0802a47711..aff3f066b247dacd42c09cdd2a71b02e3ac524f9 100644
--- a/doc/release/monthly.md
+++ b/doc/release/monthly.md
@@ -37,9 +37,9 @@ template are explained below:
 
 ### Xth: (6 working days before the 22nd)
 
-- [ ] Merge CE `master` into EE `master` via merge request (#LINK)
 - [ ] Determine QA person and notify this person
 - [ ] Check the tasks in [how to rc1 guide](https://dev.gitlab.org/gitlab/gitlabhq/blob/master/doc/release/howto_rc1.md) and delegate tasks if necessary
+- [ ] Merge CE `master` into EE `master` via merge request (#LINK)
 - [ ] Create CE and EE RC1 versions (#LINK)
 - [ ] Build RC1 packages
 
@@ -54,21 +54,25 @@ template are explained below:
 - [ ] Update GitLab.com with RC1
 - [ ] Create the regression issue in the CE issue tracker:
 
-    > This is a meta issue to index possible regressions in this monthly release
-    > and any patch versions.
-    >
-    > Please do not raise or discuss issues directly in this issue but link to
-    > issues that might warrant a patch release. If there is a Merge Request
-    > that fixes the issue, please link to that as well.
-    >
-    > Please only post one regression issue and/or merge request per comment.
-    > Comments will be updated by the release manager as they are addressed.
+    ```
+    This is a meta issue to index possible regressions in this monthly release
+    and any patch versions.
+
+    Please do not raise or discuss issues directly in this issue but link to
+    issues that might warrant a patch release. If there is a Merge Request
+    that fixes the issue, please link to that as well.
+
+    Please only post one regression issue and/or merge request per comment.
+    Comments will be updated by the release manager as they are addressed.
+    ```
 
 - [ ] Tweet about RC1 release:
 
-    > GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable
-    > Use at your own risk. Please link regressions issues from
-    > LINK_TO_REGRESSION_ISSUE
+    ```
+    GitLab x.y.0.rc1 is available: https://packages.gitlab.com/gitlab/unstable
+    Use at your own risk. Please link regressions issues from
+    LINK_TO_REGRESSION_ISSUE
+    ```
 
 ### Xth: (3 working days before the 22nd)
 
@@ -155,7 +159,7 @@ Please do not raise issues directly in this issue but link to issues that might
 The decision to create a patch release or not is with the release manager who is assigned to this issue.
 The release manager will comment here about the plans for patch releases.
 
-Assign the issue to the release manager and at mention all members of gitlab core team. If there are any known bugs in the release add them immediately.
+Assign the issue to the release manager and at mention all members of GitLab core team. If there are any known bugs in the release add them immediately.
 
 ## Tweet about RC1
 
diff --git a/doc/release/patch.md b/doc/release/patch.md
index 6aa11b283df3d12e3f86c6fe7b1e0147fbb45561..3022e375acac13590d1a959043fd2dafd1a2705a 100644
--- a/doc/release/patch.md
+++ b/doc/release/patch.md
@@ -1,21 +1,46 @@
 # Things to do when doing a patch release
 
-NOTE: This is a guide for GitLab developers. If you are trying to install GitLab see the latest stable [installation guide](install/installation.md) and if you are trying to upgrade, see the [upgrade guides](update).
+NOTE: This is a guide for GitLab developers. If you are trying to install GitLab
+see the latest stable [installation guide](install/installation.md) and if you
+are trying to upgrade, see the [upgrade guides](update).
 
 ## When to do a patch release
 
-Do a patch release when there is a critical regression that needs to be addresses before the next monthly release.
-
-Otherwise include it in the monthly release and note there was a regression fix in the release announcement.
+Patch releases are done as-needed in order to fix regressions in the current
+major release that cannot or should not wait until the next major release.
+What's included and when to release is at the discretion of the release manager.
 
 ## Release Procedure
 
+### Create a patch issue
+
+Create an issue in the GitLab CE project. Name it "Release x.y.z", tag it with
+the `release` label, and assign it to the milestone of the corresponding major
+release.
+
+Use the following template:
+
+```
+- Picked into respective `stable` branches:
+- [ ] Merge `x-y-stable` into `x-y-stable-ee`
+- [ ] release-tools: `x.y.z`
+- gitlab-omnibus
+  - [ ] `x.y.z+ee.0`
+  - [ ] `x.y.z+ce.0`
+- [ ] Deploy
+- [ ] Add patch notice to [x.y regressions]()
+- [ ] [Blog post]()
+- [ ] [Tweet]()
+- [ ] Add entry to version.gitlab.com
+```
+
+Update the issue with links to merge requests that need to be/have been picked
+into the `stable` branches.
+
 ### Preparation
 
 1. Verify that the issue can be reproduced
 1. Note in the 'GitLab X.X regressions' that you will create a patch
-1. Create an issue on private GitLab development server
-1. Name the issue "Release X.X.X CE and X.X.X EE", this will make searching easier
 1. Fix the issue on a feature branch, do this on the private GitLab development server
 1. If it is a security issue, then assign it to the release manager and apply a 'security' label
 1. Consider creating and testing workarounds
@@ -25,7 +50,6 @@ Otherwise include it in the monthly release and note there was a regression fix
 1. For EE, update the CHANGELOG-EE if it is EE specific fix. Otherwise, merge the stable CE branch and add to CHANGELOG-EE "Merge community edition changes for version X.X.X"
 1. Merge CE stable branch into EE stable branch
 
-
 ### Bump version
 
 Get release tools
@@ -54,4 +78,4 @@ bundle exec rake release["x.x.x"]
 1. Note in the 'GitLab X.X regressions' issue that the patch was published (CE only)
 1. Create the 'x.y.0' version on version.gitlab.com
 1. [Create new AMIs](https://dev.gitlab.org/gitlab/AMI/blob/master/README.md)
-1. Create a new patch release issue for the next potential release
\ No newline at end of file
+1. Create a new patch release issue for the next potential release
diff --git a/doc/release/security.md b/doc/release/security.md
index 60bcfbb6da5b9f94e680076b268d5d0685225bee..b1a62b333e64f1489986d4873c51c81887bcf54f 100644
--- a/doc/release/security.md
+++ b/doc/release/security.md
@@ -8,7 +8,7 @@ Do a security release when there is a critical issue that needs to be addresses
 
 ## Security vulnerability disclosure
 
-Please report suspected security vulnerabilities in private to <support@gitlab.com>, also see the [disclosure section on the GitLab.com website](http://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
+Please report suspected security vulnerabilities in private to <support@gitlab.com>, also see the [disclosure section on the GitLab.com website](https://about.gitlab.com/disclosure/). Please do NOT create publicly viewable issues for suspected security vulnerabilities.
 
 ## Release Procedure
 
@@ -25,7 +25,7 @@ Please report suspected security vulnerabilities in private to <support@gitlab.c
 1. Send tweets about the release from `@gitlabhq`
 1. Send out an email to [the community google mailing list](https://groups.google.com/forum/#!forum/gitlabhq)
 1. Post a signed copy of our complete announcement to [oss-security](http://www.openwall.com/lists/oss-security/) and request a CVE number. CVE is only needed for bugs that allow someone to own the server (Remote Code Execution) or access to code of projects they are not a member of.
-1. Add the security researcher to the [Security Researcher Acknowledgments list](http://about.gitlab.com/vulnerability-acknowledgements/)
+1. Add the security researcher to the [Security Researcher Acknowledgments list](https://about.gitlab.com/vulnerability-acknowledgements/)
 1. Thank the security researcher in an email for their cooperation
 1. Update the blog post and the CHANGELOG when we receive the CVE number
 
diff --git a/doc/ssh/README.md b/doc/ssh/README.md
index 0bdb4070e745e031116b5d3400ec011cb8717ead..9753504ac8be72b46121aeb010518e073191e46e 100644
--- a/doc/ssh/README.md
+++ b/doc/ssh/README.md
@@ -77,7 +77,7 @@ Deploy keys can be shared between projects, you just need to add them to each pr
 
 ### Eclipse
 
-How to add your ssh key to Eclipse: http://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
+How to add your ssh key to Eclipse: https://wiki.eclipse.org/EGit/User_Guide#Eclipse_SSH_Configuration
 
 ## Tip: Non-default OpenSSH key file names or locations
 
diff --git a/doc/update/6.x-or-7.x-to-7.14.md b/doc/update/6.x-or-7.x-to-7.14.md
index b34fb12da6f618293d0f77ac437ac79462e757c8..4516a1020847b2e915686dcf1626f26b6ca7c1b4 100644
--- a/doc/update/6.x-or-7.x-to-7.14.md
+++ b/doc/update/6.x-or-7.x-to-7.14.md
@@ -47,7 +47,7 @@ Download and compile Ruby:
 
 ```bash
 mkdir /tmp/ruby && cd /tmp/ruby
-curl --progress http://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.6.tar.gz | tar xz
+curl --progress https://cache.ruby-lang.org/pub/ruby/2.1/ruby-2.1.6.tar.gz | tar xz
 cd ruby-2.1.6
 ./configure --disable-install-rdoc
 make
diff --git a/doc/update/8.1-to-8.2.md b/doc/update/8.1-to-8.2.md
index 3772f624e9860ee4df7b26627106b6657b2504fd..7b228d6a22fcfd82e644e9cb6407952ab01175fd 100644
--- a/doc/update/8.1-to-8.2.md
+++ b/doc/update/8.1-to-8.2.md
@@ -2,7 +2,8 @@
 
 **NOTE:** GitLab 8.0 introduced several significant changes related to
 installation and configuration which *are not duplicated here*. Be sure you're
-already running a working version of 8.0 before proceeding with this guide.
+already running a working version of at least 8.0 before proceeding with this
+guide.
 
 ### 0. Double-check your Git version
 
@@ -67,7 +68,7 @@ sudo -u git -H git checkout 8-2-stable-ee
 ```bash
 cd /home/git/gitlab-shell
 sudo -u git -H git fetch
-sudo -u git -H git checkout v2.6.5
+sudo -u git -H git checkout v2.6.8
 ```
 
 ### 5. Replace gitlab-git-http-server with gitlab-workhorse
@@ -80,7 +81,7 @@ from GitLab 8.1.
 cd /home/git
 sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-workhorse.git
 cd gitlab-workhorse
-sudo -u git -H git checkout 0.3.1
+sudo -u git -H git checkout 0.4.2
 sudo -u git -H make
 ```
 
@@ -165,12 +166,12 @@ To make sure you didn't miss anything run a more thorough check:
 
 If all items are green, then congratulations, the upgrade is complete!
 
-## Things went south? Revert to previous version (8.0)
+## Things went south? Revert to previous version (8.1)
 
 ### 1. Revert the code to the previous version
 
-Follow the [upgrade guide from 7.14 to 8.0](7.14-to-8.0.md), except for the database migration
-(The backup is already migrated to the previous version)
+Follow the [upgrade guide from 8.0 to 8.1](8.0-to-8.1.md), except for the
+database migration (the backup is already migrated to the previous version).
 
 ### 2. Restore from the backup
 
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index 593722eb01ff43e7af394446ca7f52827441742c..957354decb75448bcefe7a2de1cd09b3aa37ff0c 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -6,7 +6,8 @@ For example from 7.14.0 to 7.14.3, also see the [semantic versioning specificati
 ### 0. Backup
 
 It's useful to make a backup just in case things go south:
-(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab user on the database version)
+(With MySQL, this may require granting "LOCK TABLES" privileges to the GitLab
+user on the database version)
 
 ```bash
 cd /home/git/gitlab
@@ -15,19 +16,23 @@ sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
 
 ### 1. Stop server
 
-    sudo service gitlab stop
+```bash
+sudo service gitlab stop
+```
 
 ### 2. Get latest code for the stable branch
 
+In the commands below, 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`).
+
 ```bash
 cd /home/git/gitlab
 sudo -u git -H git fetch --all
 sudo -u git -H git checkout -- Gemfile.lock db/schema.rb
 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
 
@@ -37,12 +42,20 @@ sudo -u git -H git fetch
 sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`cat /home/git/gitlab/GITLAB_SHELL_VERSION`
 ```
 
-### 4. Install libs, migrations, etc.
+### 4. Update gitlab-workhorse to the corresponding version
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch
+sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b v`cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
+```
+
+### 5. Install libs, migrations, etc.
 
 ```bash
 cd /home/git/gitlab
 
-#PostgreSQL
+# PostgreSQL
 sudo -u git -H bundle install --without development test mysql --deployment
 
 # MySQL
@@ -52,19 +65,25 @@ sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
 sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
 ```
 
-### 5. Start application
+### 6. Start application
 
-    sudo service gitlab start
-    sudo service nginx restart
+```bash
+sudo service gitlab start
+sudo service nginx restart
+```
 
-### 6. Check application status
+### 7. Check application status
 
 Check if GitLab and its environment are configured correctly:
 
-    sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```bash
+sudo -u git -H bundle exec rake gitlab:env:info RAILS_ENV=production
+```
 
 To make sure you didn't miss anything run a more thorough check with:
 
-    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```bash
+sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+```
 
 If all items are green, then congratulations upgrade complete!
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 5b8d72dfd340f60368827d46844cbf4cc595a2f8..a6b4d9511884f8c1f1a9cb21389bbd0cf7e91a62 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -13,5 +13,8 @@
 - [Project users](add-user/add-user.md)
 - [Protected branches](protected_branches.md)
 - [Web Editor](web_editor.md)
+- [Releases](releases.md)
+- [Milestones](milestones.md)
 - [Merge Requests](merge_requests.md)
 - ["Work In Progress" Merge Requests](wip_merge_requests.md)
+- [Manage large binaries with Git LFS](lfs/manage_large_binaries_with_git_lfs.md)
diff --git a/doc/workflow/award_emoji.png b/doc/workflow/award_emoji.png
new file mode 100644
index 0000000000000000000000000000000000000000..fb26ee043930b2a06f4b93dd3918c041e2f32033
Binary files /dev/null and b/doc/workflow/award_emoji.png differ
diff --git a/doc/workflow/gitlab_flow.md b/doc/workflow/gitlab_flow.md
index 9a24a1e252acef81ecc3213d23c60517e83b0969..8965e5b365442da9e3a0a4d3f30760eedecd5c10 100644
--- a/doc/workflow/gitlab_flow.md
+++ b/doc/workflow/gitlab_flow.md
@@ -7,7 +7,7 @@ This allows a wide variety of branching strategies and workflows.
 Almost all of these are an improvement over the methods used before git.
 But many organizations end up with a workflow that is not clearly defined, overly complex or not integrated with issue tracking systems.
 Therefore we propose the GitLab flow as clearly defined set of best practices.
-It combines [feature driven development](http://en.wikipedia.org/wiki/Feature-driven_development) and [feature branches](http://martinfowler.com/bliki/FeatureBranch.html) with issue tracking.
+It combines [feature driven development](https://en.wikipedia.org/wiki/Feature-driven_development) and [feature branches](http://martinfowler.com/bliki/FeatureBranch.html) with issue tracking.
 
 Organizations coming to git from other version control systems frequently find it hard to develop an effective workflow.
 This article describes the GitLab flow that integrates the git workflow with an issue tracking system.
@@ -91,7 +91,7 @@ This workflow where commits only flow downstream ensures that everything has bee
 If you need to cherry-pick a commit with a hotfix it is common to develop it on a feature branch and merge it into master with a merge request, do not delete the feature branch.
 If master is good to go (it should be if you a practicing [continuous delivery](http://martinfowler.com/bliki/ContinuousDelivery.html)) you then merge it to the other branches.
 If this is not possible because more manual testing is required you can send merge requests from the feature branch to the downstream branches.
-An 'extreme' version of environment branches are setting up an environment for each feature branch as done by [Teatro](http://teatro.io/).
+An 'extreme' version of environment branches are setting up an environment for each feature branch as done by [Teatro](https://teatro.io/).
 
 ## Release branches with GitLab flow
 
@@ -104,7 +104,7 @@ By branching as late as possible you minimize the time you have to apply bug fix
 After a release branch is announced, only serious bug fixes are included in the release branch.
 If possible these bug fixes are first merged into master and then cherry-picked into the release branch.
 This way you can't forget to cherry-pick them into master and encounter the same bug on subsequent releases.
-This is called an 'upstream first' policy that is also practiced by [Google](http://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first) and [Red Hat](http://www.redhat.com/about/news/archive/2013/5/a-community-for-using-openstack-with-red-hat-rdo).
+This is called an 'upstream first' policy that is also practiced by [Google](https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first) and [Red Hat](https://www.redhat.com/about/news/archive/2013/5/a-community-for-using-openstack-with-red-hat-rdo).
 Every time a bug-fix is included in a release branch the patch version is raised (to comply with [Semantic Versioning](http://semver.org/)) by setting a new tag.
 Some projects also have a stable branch that points to the same commit as the latest released branch.
 In this flow it is not common to have a production branch (or git flow master branch).
@@ -200,7 +200,7 @@ And to understand a change in context one can always look at the merge commit th
 
 After you merge multiple commits from a feature branch into the master branch this is harder to undo.
 If you would have squashed all the commits into one you could have just reverted this commit but as we indicated you should not rebase commits after they are pushed.
-Fortunately [reverting a merge made some time ago](http://git-scm.com/blog/2010/03/02/undoing-merges.html) can be done with git.
+Fortunately [reverting a merge made some time ago](https://git-scm.com/blog/2010/03/02/undoing-merges.html) can be done with git.
 This however, requires having specific merge commits for the commits your want to revert.
 If you revert a merge and you change your mind, revert the revert instead of merging again since git will not allow you to merge the code again otherwise.
 
@@ -215,7 +215,7 @@ With git you can also rebase your feature branch commits to order them after the
 This prevents creating a merge commit when merging master into your feature branch and creates a nice linear history.
 However, just like with squashing you should never rebase commits you have pushed to a remote server.
 This makes it impossible to rebase work in progress that you already shared with your team which is something we recommend.
-When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](http://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
+When using rebase to keep your feature branch updated you [need to resolve similar conflicts again and again](https://blogs.atlassian.com/2013/10/git-team-workflows-merge-or-rebase/).
 You can reuse recorded resolutions (rerere) sometimes, but without rebasing you only have to solve the conflicts one time and you鈥檙e set.
 There has to be a better way to avoid many merge commits.
 
@@ -244,13 +244,12 @@ Developing software happen in small messy steps and it is OK to have your histor
 You can use tools to view the network graphs of commits and understand the messy history that created your code.
 If you rebase code the history is incorrect, and there is no way for tools to remedy this because they can't deal with changing commit identifiers.
 
-## Voting on merge requests
+## Award emojis on issues and merge requests
 
-![Voting slider in GitLab](voting_slider.png)
+![Emoji bar in GitLab](award_emoji.png)
 
-It is common to voice approval or disapproval by using +1 or -1 emoticons.
-In GitLab the +1 and -1 are aggregated and shown at the top of the merge request.
-As a rule of thumb anything that doesn't have two times more +1's than -1's is suspect and should not be merged yet.
+It is common to voice approval or disapproval by using +1 or -1. In GitLab you
+can use emojis to give a virtual high five on issues and merge requests.
 
 ## Pushing and removing branches
 
@@ -307,7 +306,7 @@ When initiating a feature branch, always start with an up to date master to bran
 If you know beforehand that your work absolutely depends on another branch you can also branch from there.
 If you need to merge in another branch after starting explain the reason in the merge commit.
 If you have not pushed your commits to a shared location yet you can also rebase on master or another feature branch.
-Do not merge in upstream if your code will work and merge cleanly without doing so, Linus even says that [you should never merge in upstream at random points, only at major releases](http://lwn.net/Articles/328438/).
+Do not merge in upstream if your code will work and merge cleanly without doing so, Linus even says that [you should never merge in upstream at random points, only at major releases](https://lwn.net/Articles/328438/).
 Merging only when needed prevents creating merge commits in your feature branch that later end up littering the master history.
 
 ### References
diff --git a/doc/workflow/importing/migrating_from_svn.md b/doc/workflow/importing/migrating_from_svn.md
index 485db4834e97c635d546cbfeb02c14e893051cfd..1938ccd0c264c42b2d42b7772f85980e1d1d262d 100644
--- a/doc/workflow/importing/migrating_from_svn.md
+++ b/doc/workflow/importing/migrating_from_svn.md
@@ -6,9 +6,9 @@ Git is a distributed version control system.
 There are some major differences between the two, for more information consult your favorite search engine.
 
 Git has tools for migrating SVN repositories to git, namely `git svn`. You can read more about this at
-[git documentation pages](http://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
+[git documentation pages](https://git-scm.com/book/en/Git-and-Other-Systems-Git-and-Subversion).
 
-Apart from the [official git documentation](http://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
+Apart from the [official git documentation](https://git-scm.com/book/en/Git-and-Other-Systems-Migrating-to-Git) there is also
 user created step by step guide for migrating from SVN to GitLab.
 
 [Benjamin New](https://github.com/leftclickben) wrote [a guide that shows how to do a migration](https://gist.github.com/leftclickben/322b7a3042cbe97ed2af). Mirrors can be found [here](https://gitlab.com/snippets/2168) and [here](https://gist.github.com/maxlazio/f1b593b0d00aa966e9ca).
diff --git a/doc/workflow/lfs/lfs_administration.md b/doc/workflow/lfs/lfs_administration.md
new file mode 100644
index 0000000000000000000000000000000000000000..5076b2697a355010a8e2350643627f05d6bf415a
--- /dev/null
+++ b/doc/workflow/lfs/lfs_administration.md
@@ -0,0 +1,41 @@
+# GitLab Git LFS Administration
+
+Documentation on how to use Git LFS are under [Managing large binary files with Git LFS doc](manage_large_binaries_with_git_lfs.md).
+
+## Requirements
+
+* Git LFS is supported in GitLab starting with version 8.2.
+* Users need to install [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up.
+
+## Configuration
+
+Git LFS objects can be large in size. By default, they are stored on the server GitLab is installed on.
+
+There are two configuration options to help GitLab server administrators:
+
+* Enabling/disabling Git LFS support
+* Changing the location of LFS object storage
+
+### Omnibus packages
+
+In `/etc/gitlab/gitlab.rb`:
+
+```ruby
+gitlab_rails['lfs_enabled'] = false
+gitlab_rails['lfs_storage_path'] = "/mnt/storage/lfs-objects"
+```
+
+### Installations from source
+
+In `config/gitlab.yml`:
+
+```yaml
+  lfs:
+    enabled: false
+    storage_path: /mnt/storage/lfs-objects
+```
+
+## Known limitations
+
+* Currently, storing GitLab Git LFS objects on a non-local storage (like S3 buckets) is not supported
+* Currently, removing LFS objects from GitLab Git LFS storage is not supported
diff --git a/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
new file mode 100644
index 0000000000000000000000000000000000000000..b59e92cb31798cbe20963082300f17ed1d7c19cc
--- /dev/null
+++ b/doc/workflow/lfs/manage_large_binaries_with_git_lfs.md
@@ -0,0 +1,126 @@
+# Git LFS
+
+Managing large files such as audio, video and graphics files has always been one of the shortcomings of Git.
+The general recommendation is to not have Git repositories larger than 1GB to preserve performance.
+
+GitLab already supports [managing large files with git annex](http://doc.gitlab.com/ee/workflow/git_annex.html) (EE only), however in certain
+environments it is not always convenient to use different commands to differentiate between the large files and regular ones.
+
+Git LFS makes this simpler for the end user by removing the requirement to learn new commands.
+
+## How it works
+
+Git LFS client talks with the GitLab server over HTTPS. It uses HTTP Basic Authentication to authorize client requests.
+Once the request is authorized, Git LFS client receives instructions from where to fetch or where to push the large file.
+
+## GitLab server configuration
+
+Documentation for GitLab instance administrators is under [LFS administration doc](lfs_administration.md).
+
+## Requirements
+
+* Git LFS is supported in GitLab starting with version 8.2
+* [Git LFS client](https://git-lfs.github.com) version 1.0.1 and up
+
+## Known limitations
+
+* Git LFS v1 original API is not supported since it was deprecated early in LFS development
+* When SSH is set as a remote, Git LFS objects still go through HTTPS
+* Any Git LFS request will ask for HTTPS credentials to be provided so good Git credentials store is recommended
+* Git LFS always assumes HTTPS so if you have GitLab server on HTTP you will have to add the URL to Git config manually (see #troubleshooting)
+
+## Using Git LFS
+
+Lets take a look at the workflow when you need to check large files into your Git repository with Git LFS:
+For example, if you want to upload a very large file and check it into your Git repository:
+
+```bash
+git clone git@gitlab.example.com:group/project.git
+git lfs init                          # initialize the Git LFS project project
+git lfs track "*.iso"                 # select the file extensions that you want to treat as large files
+```
+
+Once a certain file extension is marked for tracking as a LFS object you can use Git as usual without having to redo the command to track a file with the same extension:
+
+```bash
+cp ~/tmp/debian.iso ./                # copy a large file into the current directory
+git add .                             # add the large file to the project
+git commit -am "Added Debian iso"     # commit the file meta data
+git push origin master                # sync the git repo and large file to the GitLab server
+```
+
+Cloning the repository works the same as before. Git automatically detects the LFS-tracked files and clones them via HTTP. If you performed the git clone command with a SSH URL, you have to enter your GitLab credentials for HTTP authentication.
+
+```bash
+git clone git@gitlab.example.com:group/project.git
+```
+
+If you already cloned the repository and you want to get the latest LFS object that are on the remote repository, eg. from branch `master`:
+
+```bash
+git lfs fetch master
+```
+
+## Troubleshooting
+
+### error: Repository or object not found
+
+There are a couple of reasons why this error can occur:
+
+* You don't have permissions to access certain LFS object
+
+Check if you have permissions to push to the project or fetch from the project.
+
+* Project is not allowed to access the LFS object
+
+LFS object you are trying to push to the project or fetch from the project is not available to the project anymore.
+Probably the object was removed from the server.
+
+* Local git repository is using deprecated LFS API
+
+### Invalid status for <url> : 501
+
+Git LFS will log the failures into a log file.
+To view this log file, while in project directory:
+
+```bash
+git lfs logs last
+```
+
+If the status `error 501` is shown, it is because:
+
+* Git LFS support is not enabled on the GitLab server. Check with your GitLab administrator why Git LFS is not enabled on the server. See [LFS administration documentation](lfs_administration.md) for instructions on how to enable LFS support.
+
+* Git LFS client version is not supported by GitLab server. Check your Git LFS version with `git lfs version`. Check the Git config of the project for traces of deprecated API with `git lfs -l`. If `batch = false` is set in the config, remove the line and try to update your Git LFS client. Only version 1.0.1 and newer are supported.
+
+### getsockopt: connection refused
+
+If you push a LFS object to a project and you receive an error similar to: `Post <URL>/info/lfs/objects/batch: dial tcp IP: getsockopt: connection refused`,
+the LFS client is trying to reach GitLab through HTTPS. However, your GitLab instance is being served on HTTP.
+
+This behaviour is caused by Git LFS using HTTPS connections by default when a `lfsurl` is not set in the Git config.
+
+To prevent this from happening, set the lfs url in project Git config:
+
+```bash
+
+git config --add lfs.url "http://gitlab.example.com/group/project.git/info/lfs/objects/batch"
+```
+
+### Credentials are always required when pushing an object
+
+Given that Git LFS uses HTTP Basic Authentication to authenticate the user pushing the LFS object on every push for every object, user HTTPS credentials are required.
+
+By default, Git has support for remembering the credentials for each repository you use. This is described in [Git credentials man pages](https://git-scm.com/docs/gitcredentials).
+
+For example, you can tell Git to remember the password for a period of time in which you expect to push the objects:
+
+```bash
+git config --global credential.helper 'cache --timeout=3600'
+```
+
+This will remember the credentials for an hour after which Git operations will require re-authentication.
+
+If you are using OS X you can use `osxkeychain` to store and encrypt your credentials. For Windows, you can use `wincred` or Microsoft's [Git Credential Manager for Windows](https://github.com/Microsoft/Git-Credential-Manager-for-Windows/releases).
+
+More details about various methods of storing the user credentials can be found on [Git Credential Storage documentation](https://git-scm.com/book/en/v2/Git-Tools-Credential-Storage).
\ No newline at end of file
diff --git a/doc/workflow/merge_requests.md b/doc/workflow/merge_requests.md
index 751e19da7f19a5820bb40e8b4f2a79eb8bb7cc0c..6d57b5d98cdc656c1ab8b56080eac359ba755c00 100644
--- a/doc/workflow/merge_requests.md
+++ b/doc/workflow/merge_requests.md
@@ -38,3 +38,15 @@ To check out a particular merge request:
 ```
 $ git checkout origin/merge-requests/1
 ```
+
+## Ignore whitespace changes in Merge Request diff view
+
+![MR diff](merge_requests/merge_request_diff.png)
+
+It you add `w=1` option to URL, you can see diff without whitespace changes.
+
+![MR diff without whitespace](merge_requests/merge_request_diff_without_whitespace.png)
+
+It is also working on commits compare view.
+
+![Commit Compare](merge_requests/commit_compare.png)
diff --git a/doc/workflow/merge_requests/commit_compare.png b/doc/workflow/merge_requests/commit_compare.png
new file mode 100644
index 0000000000000000000000000000000000000000..46b3a56a59b9cdf0317d7ae8c1903a65eca2c5af
Binary files /dev/null and b/doc/workflow/merge_requests/commit_compare.png differ
diff --git a/doc/workflow/merge_requests/merge_request_diff.png b/doc/workflow/merge_requests/merge_request_diff.png
new file mode 100644
index 0000000000000000000000000000000000000000..ed08ae91bec73772c3cd51521a46edfea9aace93
Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff.png differ
diff --git a/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png
new file mode 100644
index 0000000000000000000000000000000000000000..67d67a64d122ffa00c76a34a095f56b6f48adb01
Binary files /dev/null and b/doc/workflow/merge_requests/merge_request_diff_without_whitespace.png differ
diff --git a/doc/workflow/milestones.md b/doc/workflow/milestones.md
new file mode 100644
index 0000000000000000000000000000000000000000..dff36899aec54234aa4fedf72d92296246160ddd
--- /dev/null
+++ b/doc/workflow/milestones.md
@@ -0,0 +1,13 @@
+# Milestones
+
+Milestones allow you to organize issues and merge requests into a cohesive group, optionally setting a due date. 
+A common use is keeping track of an upcoming software version. Milestones are created per-project.
+
+![milestone form](milestones/form.png)
+
+## Groups and milestones
+
+You can create a milestone for several projects in the same group simultaneously. 
+On the group's milestones page, you will be able to see the status of that milestone across all of the selected projects.
+
+![group milestone form](milestones/group_form.png)
diff --git a/doc/workflow/milestones/form.png b/doc/workflow/milestones/form.png
new file mode 100644
index 0000000000000000000000000000000000000000..de44c1ffc1a44c21e7d6eff77ff8752d84b6ea8f
Binary files /dev/null and b/doc/workflow/milestones/form.png differ
diff --git a/doc/workflow/milestones/group_form.png b/doc/workflow/milestones/group_form.png
new file mode 100644
index 0000000000000000000000000000000000000000..38862dcca6895c28a09c6173fc8a2d7b1552b528
Binary files /dev/null and b/doc/workflow/milestones/group_form.png differ
diff --git a/doc/workflow/releases.md b/doc/workflow/releases.md
new file mode 100644
index 0000000000000000000000000000000000000000..6176784fc57f02954250a110ca8611e981c0f2f7
--- /dev/null
+++ b/doc/workflow/releases.md
@@ -0,0 +1,20 @@
+# Releases
+
+You can turn any git tag into a release, by adding a note to it.
+Release notes behave like any other markdown form in GitLab so you can write text and drag-n-drop files to it.
+Release notes are stored in the database of GitLab. 
+
+There are several ways to add release notes: 
+
+* In the interface, when you create a new git tag with GitLab
+* In the interface, by adding a note to an existing git tag
+* with the GitLab API
+
+## New tag page with release notes text area
+
+![new_tag](releases/new_tag.png)
+
+## Tags page with button to add or edit release notes for existing git tag
+
+![tags](releases/tags.png)
+
diff --git a/doc/workflow/releases/new_tag.png b/doc/workflow/releases/new_tag.png
new file mode 100644
index 0000000000000000000000000000000000000000..e2b64bfe17fb95f221cbd57ec3da684b5e47ee39
Binary files /dev/null and b/doc/workflow/releases/new_tag.png differ
diff --git a/doc/workflow/releases/tags.png b/doc/workflow/releases/tags.png
new file mode 100644
index 0000000000000000000000000000000000000000..aca91906c68a439d0987085db345cb82c04a51b4
Binary files /dev/null and b/doc/workflow/releases/tags.png differ
diff --git a/doc/workflow/voting_slider.png b/doc/workflow/voting_slider.png
deleted file mode 100644
index 4c660ef95937c66924a2f01373cc4efed4eae831..0000000000000000000000000000000000000000
Binary files a/doc/workflow/voting_slider.png and /dev/null differ
diff --git a/features/group/members.feature b/features/group/members.feature
new file mode 100644
index 0000000000000000000000000000000000000000..1f9514bac39ed994f3f50602fc40d279e30e042e
--- /dev/null
+++ b/features/group/members.feature
@@ -0,0 +1,105 @@
+Feature: Group Members
+  Background:
+    Given I sign in as "John Doe"
+    And "John Doe" is owner of group "Owned"
+    And "John Doe" is guest of group "Guest"
+
+  @javascript
+  Scenario: I should add user to group "Owned"
+    Given User "Mary Jane" exists
+    When I visit group "Owned" members page
+    And I select user "Mary Jane" from list with role "Reporter"
+    Then I should see user "Mary Jane" in team list
+
+  @javascript
+  Scenario: Add user to group
+    Given gitlab user "Mike"
+    When I visit group "Owned" members page
+    When I select "Mike" as "Reporter"
+    Then I should see "Mike" in team list as "Reporter"
+
+  @javascript
+  Scenario: Ignore add user to group when is already Owner
+    Given gitlab user "Mike"
+    When I visit group "Owned" members page
+    When I select "Mike" as "Reporter"
+    Then I should see "Mike" in team list as "Owner"
+
+  @javascript
+  Scenario: Invite user to group
+    When I visit group "Owned" members page
+    When I select "sjobs@apple.com" as "Reporter"
+    Then I should see "sjobs@apple.com" in team list as invited "Reporter"
+
+  @javascript
+  Scenario: Edit group member permissions
+    Given "Mary Jane" is guest of group "Owned"
+    And I visit group "Owned" members page
+    When I change the "Mary Jane" role to "Developer"
+    Then I should see "Mary Jane" as "Developer"
+
+  # Leave
+
+  @javascript
+  Scenario: Owner should be able to remove himself from group if he is not the last owner
+    Given "Mary Jane" is owner of group "Owned"
+    When I visit group "Owned" members page
+    Then I should see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+    When I click on the "Remove User From Group" button for "John Doe"
+    And I visit group "Owned" members page
+    Then I should not see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+
+  @javascript
+  Scenario: Owner should not be able to remove himself from group if he is the last owner
+    Given "Mary Jane" is guest of group "Owned"
+    When I visit group "Owned" members page
+    Then I should see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+    Then I should not see the "Remove User From Group" button for "John Doe"
+
+  @javascript
+  Scenario: Guest should be able to remove himself from group
+    Given "Mary Jane" is guest of group "Guest"
+    When I visit group "Guest" members page
+    Then I should see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+    When I click on the "Remove User From Group" button for "John Doe"
+    When I visit group "Guest" members page
+    Then I should not see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+
+  @javascript
+  Scenario: Guest should be able to remove himself from group even if he is the only user in the group
+    When I visit group "Guest" members page
+    Then I should see user "John Doe" in team list
+    When I click on the "Remove User From Group" button for "John Doe"
+    When I visit group "Guest" members page
+    Then I should not see user "John Doe" in team list
+
+  # Remove others
+
+  Scenario: Owner should be able to remove other users from group
+    Given "Mary Jane" is owner of group "Owned"
+    When I visit group "Owned" members page
+    Then I should see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+    When I click on the "Remove User From Group" button for "Mary Jane"
+    When I visit group "Owned" members page
+    Then I should see user "John Doe" in team list
+    Then I should not see user "Mary Jane" in team list
+
+  Scenario: Guest should not be able to remove other users from group
+    Given "Mary Jane" is guest of group "Guest"
+    When I visit group "Guest" members page
+    Then I should see user "John Doe" in team list
+    Then I should see user "Mary Jane" in team list
+    Then I should not see the "Remove User From Group" button for "Mary Jane"
+
+  Scenario: Search member by name
+    Given "Mary Jane" is guest of group "Guest"
+    And I visit group "Guest" members page
+    When I search for 'Mary' member
+    Then I should see user "Mary Jane" in team list
+    Then I should not see user "John Doe" in team list
diff --git a/features/group/milestones.feature b/features/group/milestones.feature
new file mode 100644
index 0000000000000000000000000000000000000000..62ea66a783cc16fda8ca60e9209fff781ce63999
--- /dev/null
+++ b/features/group/milestones.feature
@@ -0,0 +1,30 @@
+Feature: Group Milestones
+  Background:
+    Given I sign in as "John Doe"
+    And "John Doe" is owner of group "Owned"
+
+  Scenario: I should see group "Owned" milestone index page with no milestones
+    When I visit group "Owned" page
+    And I click on group milestones
+    Then I should see group milestones index page has no milestones
+
+  Scenario: I should see group "Owned" milestone index page with milestones
+    Given Group has projects with milestones
+    When I visit group "Owned" page
+    And I click on group milestones
+    Then I should see group milestones index page with milestones
+
+  Scenario: I should see group "Owned" milestone show page
+    Given Group has projects with milestones
+    When I visit group "Owned" page
+    And I click on group milestones
+    And I click on one group milestone
+    Then I should see group milestone with descriptions and expiry date
+    And I should see group milestone with all issues and MRs assigned to that milestone
+
+  Scenario: Create multiple milestones with one form
+    Given I visit group "Owned" milestones page
+    And I click new milestone button
+    And I fill milestone name
+    When I press create mileston button
+    Then milestone in each project should be created
diff --git a/features/groups.feature b/features/groups.feature
index db37fa3b3757b3437a1cb107620a159213062137..c803e9529803b493abdd2db4d983b5baf11864d5 100644
--- a/features/groups.feature
+++ b/features/groups.feature
@@ -2,7 +2,6 @@ Feature: Groups
   Background:
     Given I sign in as "John Doe"
     And "John Doe" is owner of group "Owned"
-    And "John Doe" is guest of group "Guest"
 
   Scenario: I should have back to group button
     When I visit group "Owned" page
@@ -24,13 +23,6 @@ Feature: Groups
     When I visit group "Owned" merge requests page
     Then I should see merge requests from group "Owned" assigned to me
 
-  @javascript
-  Scenario: I should add user to projects in group "Owned"
-    Given User "Mary Jane" exists
-    When I visit group "Owned" members page
-    And I select user "Mary Jane" from list with role "Reporter"
-    Then I should see user "Mary Jane" in team list
-
   Scenario: I should see edit group "Owned" page
     When I visit group "Owned" settings page
     And I change group "Owned" name to "new-name"
@@ -51,108 +43,6 @@ Feature: Groups
     Then I should not see group "Owned" avatar
     And I should not see the "Remove avatar" button
 
-  @javascript
-  Scenario: Add user to group
-    Given gitlab user "Mike"
-    When I visit group "Owned" members page
-    And I click link "Add members"
-    When I select "Mike" as "Reporter"
-    Then I should see "Mike" in team list as "Reporter"
-
-  @javascript
-  Scenario: Invite user to group
-    When I visit group "Owned" members page
-    And I click link "Add members"
-    When I select "sjobs@apple.com" as "Reporter"
-    Then I should see "sjobs@apple.com" in team list as invited "Reporter"
-
-  # Leave
-
-  @javascript
-  Scenario: Owner should be able to remove himself from group if he is not the last owner
-    Given "Mary Jane" is owner of group "Owned"
-    When I visit group "Owned" members page
-    Then I should see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-    When I click on the "Remove User From Group" button for "John Doe"
-    And I visit group "Owned" members page
-    Then I should not see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-
-  @javascript
-  Scenario: Owner should not be able to remove himself from group if he is the last owner
-    Given "Mary Jane" is guest of group "Owned"
-    When I visit group "Owned" members page
-    Then I should see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-    Then I should not see the "Remove User From Group" button for "John Doe"
-
-  @javascript
-  Scenario: Guest should be able to remove himself from group
-    Given "Mary Jane" is guest of group "Guest"
-    When I visit group "Guest" members page
-    Then I should see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-    When I click on the "Remove User From Group" button for "John Doe"
-    When I visit group "Guest" members page
-    Then I should not see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-
-  @javascript
-  Scenario: Guest should be able to remove himself from group even if he is the only user in the group
-    When I visit group "Guest" members page
-    Then I should see user "John Doe" in team list
-    When I click on the "Remove User From Group" button for "John Doe"
-    When I visit group "Guest" members page
-    Then I should not see user "John Doe" in team list
-
-  # Remove others
-
-  Scenario: Owner should be able to remove other users from group
-    Given "Mary Jane" is owner of group "Owned"
-    When I visit group "Owned" members page
-    Then I should see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-    When I click on the "Remove User From Group" button for "Mary Jane"
-    When I visit group "Owned" members page
-    Then I should see user "John Doe" in team list
-    Then I should not see user "Mary Jane" in team list
-
-  Scenario: Guest should not be able to remove other users from group
-    Given "Mary Jane" is guest of group "Guest"
-    When I visit group "Guest" members page
-    Then I should see user "John Doe" in team list
-    Then I should see user "Mary Jane" in team list
-    Then I should not see the "Remove User From Group" button for "Mary Jane"
-
-  Scenario: Search member by name
-    Given "Mary Jane" is guest of group "Guest"
-    And I visit group "Guest" members page
-    When I search for 'Mary' member
-    Then I should see user "Mary Jane" in team list
-    Then I should not see user "John Doe" in team list
-
-  # Group milestones
-
-  Scenario: I should see group "Owned" milestone index page with no milestones
-    When I visit group "Owned" page
-    And I click on group milestones
-    Then I should see group milestones index page has no milestones
-
-  Scenario: I should see group "Owned" milestone index page with milestones
-    Given Group has projects with milestones
-    When I visit group "Owned" page
-    And I click on group milestones
-    Then I should see group milestones index page with milestones
-
-  Scenario: I should see group "Owned" milestone show page
-    Given Group has projects with milestones
-    When I visit group "Owned" page
-    And I click on group milestones
-    And I click on one group milestone
-    Then I should see group milestone with descriptions and expiry date
-    And I should see group milestone with all issues and MRs assigned to that milestone
-
   # Group projects in settings
   Scenario: I should see all projects in the project list in settings
     Given Group "Owned" has archived project
@@ -169,4 +59,4 @@ Feature: Groups
     When I visit group "Owned" page
     Then I should see group "Owned"
     Then I should see project "Public-project"
-   
+
diff --git a/features/project/issues/award_emoji.feature b/features/project/issues/award_emoji.feature
new file mode 100644
index 0000000000000000000000000000000000000000..a9bc8ffb9bb85ff245295621c0b46937f0801628
--- /dev/null
+++ b/features/project/issues/award_emoji.feature
@@ -0,0 +1,14 @@
+Feature: Award Emoji
+  Background:
+    Given I sign in as a user
+    And I own project "Shop"
+    And project "Shop" has issue "Bugfix"
+    And I visit "Bugfix" issue page
+
+  @javascript
+  Scenario: I add and remove award in the issue
+    Given I click to emoji-picker
+    And I click to emoji in the picker
+    Then I have award added
+    And I can remove it by clicking to icon
+    
\ No newline at end of file
diff --git a/features/project/source/browse_files.feature b/features/project/source/browse_files.feature
index 69aa79f2d2462fae3a494ba539f14aa0601e88d5..e545ea63ca880efe84b78f94826df221ad421702 100644
--- a/features/project/source/browse_files.feature
+++ b/features/project/source/browse_files.feature
@@ -42,7 +42,7 @@ Feature: Project Source Browse Files
     And I fill the new branch name
     And I click on "Upload file"
     Then I can see the new text file
-    And I am redirected to the uploaded file on new branch
+    And I am redirected to the new merge request page
     And I can see the new commit message
 
   @javascript
@@ -64,7 +64,7 @@ Feature: Project Source Browse Files
     And I fill the commit message
     And I fill the new branch name
     And I click on "Commit Changes"
-    Then I am redirected to the new file on new branch
+    Then I am redirected to the new merge request page
     And I should see its new content
 
   @javascript
@@ -134,7 +134,7 @@ Feature: Project Source Browse Files
     And I fill the commit message
     And I fill the new branch name
     And I click on "Commit Changes"
-    Then I am redirected to the ".gitignore" on new branch
+    Then I am redirected to the new merge request page
     And I should see its new content
 
   @javascript  @wip
@@ -154,7 +154,7 @@ Feature: Project Source Browse Files
     And I fill the commit message
     And I fill the new branch name
     And I click on "Create directory"
-    Then I am redirected to the new directory
+    Then I am redirected to the new merge request page
 
   @javascript
   Scenario: I attempt to create an existing directory
@@ -174,12 +174,12 @@ Feature: Project Source Browse Files
     Then I see diff
 
   @javascript
-  Scenario: I can remove file and commit
+  Scenario: I can delete file and commit
     Given I click on ".gitignore" file in repo
     And I see the ".gitignore"
-    And I click on "Remove"
+    And I click on "Delete"
     And I fill the commit message
-    And I click on "Remove file"
+    And I click on "Delete file"
     Then I am redirected to the files URL
     And I don't see the ".gitignore"
 
diff --git a/features/project/team_management.feature b/features/project/team_management.feature
index 09a7df59df62df3f269448adf80750908846e939..06fb45c8bded626ecbe04dc756227bf4e1f3c7da 100644
--- a/features/project/team_management.feature
+++ b/features/project/team_management.feature
@@ -13,14 +13,12 @@ Feature: Project Team Management
 
   @javascript
   Scenario: Add user to project
-    Given I click link "Add members"
-    And I select "Mike" as "Reporter"
+    When I select "Mike" as "Reporter"
     Then I should see "Mike" in team list as "Reporter"
 
   @javascript
   Scenario: Invite user to project
-    Given I click link "Add members"
-    And I select "sjobs@apple.com" as "Reporter"
+    When I select "sjobs@apple.com" as "Reporter"
     Then I should see "sjobs@apple.com" in team list as invited "Reporter"
 
   @javascript
diff --git a/features/steps/admin/labels.rb b/features/steps/admin/labels.rb
index b45d98658bc83ac9a8894c99d005d9c97845e5d7..2d5db8f739e261e61ca6263af01963f2b4f4ce85 100644
--- a/features/steps/admin/labels.rb
+++ b/features/steps/admin/labels.rb
@@ -17,7 +17,7 @@ class Spinach::Features::AdminIssuesLabels < Spinach::FeatureSteps
 
   step 'I remove label \'bug\'' do
     page.within "#label_#{bug_label.id}" do
-      click_link 'Remove'
+      click_link 'Delete'
     end
   end
 
diff --git a/features/steps/dashboard/new_project.rb b/features/steps/dashboard/new_project.rb
index 44a4aa9844a239384b943f2f21db51d39f40b3ef..a0aad66184d1900325889ad19de831a308009eea 100644
--- a/features/steps/dashboard/new_project.rb
+++ b/features/steps/dashboard/new_project.rb
@@ -44,7 +44,6 @@ class Spinach::Features::NewProject < Spinach::FeatureSteps
     git_import_instructions = first('.js-toggle-content')
     expect(git_import_instructions).to be_visible
     expect(git_import_instructions).to have_content "Git repository URL"
-    expect(git_import_instructions).to have_content "The repository must be accessible over HTTP(S). If it is not publicly accessible, you can add authentication information to the URL:"
   end
 
   step 'I click on "Google Code"' do
diff --git a/features/steps/group/members.rb b/features/steps/group/members.rb
new file mode 100644
index 0000000000000000000000000000000000000000..0706df3aec5d709e923f27d79dbd61839beb5095
--- /dev/null
+++ b/features/steps/group/members.rb
@@ -0,0 +1,147 @@
+class Spinach::Features::GroupMembers < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedPaths
+  include SharedGroup
+  include SharedUser
+  include Select2Helper
+
+  step 'I select "Mike" as "Reporter"' do
+    user = User.find_by(name: "Mike")
+
+    page.within ".users-group-form" do
+      select2(user.id, from: "#user_ids", multiple: true)
+      select "Reporter", from: "access_level"
+    end
+
+    click_button "Add users to group"
+  end
+
+  step 'I select "Mike" as "Master"' do
+    user = User.find_by(name: "Mike")
+
+    page.within ".users-group-form" do
+      select2(user.id, from: "#user_ids", multiple: true)
+      select "Master", from: "access_level"
+    end
+
+    click_button "Add users to group"
+  end
+
+  step 'I should see "Mike" in team list as "Reporter"' do
+    page.within '.content-list' do
+      expect(page).to have_content('Mike')
+      expect(page).to have_content('Reporter')
+    end
+  end
+
+  step 'I should see "Mike" in team list as "Owner"' do
+    page.within '.content-list' do
+      expect(page).to have_content('Mike')
+      expect(page).to have_content('Owner')
+    end
+  end
+
+  step 'I select "sjobs@apple.com" as "Reporter"' do
+    page.within ".users-group-form" do
+      select2("sjobs@apple.com", from: "#user_ids", multiple: true)
+      select "Reporter", from: "access_level"
+    end
+
+    click_button "Add users to group"
+  end
+
+  step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
+    page.within '.content-list' do
+      expect(page).to have_content('sjobs@apple.com')
+      expect(page).to have_content('invited')
+      expect(page).to have_content('Reporter')
+    end
+  end
+
+  step 'I select user "Mary Jane" from list with role "Reporter"' do
+    user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
+
+    page.within ".users-group-form" do
+      select2(user.id, from: "#user_ids", multiple: true)
+      select "Reporter", from: "access_level"
+    end
+
+    click_button "Add users to group"
+  end
+
+  step 'I should see user "John Doe" in team list' do
+    expect(group_members_list).to have_content("John Doe")
+  end
+
+  step 'I should not see user "John Doe" in team list' do
+    expect(group_members_list).not_to have_content("John Doe")
+  end
+
+  step 'I should see user "Mary Jane" in team list' do
+    expect(group_members_list).to have_content("Mary Jane")
+  end
+
+  step 'I should not see user "Mary Jane" in team list' do
+    expect(group_members_list).not_to have_content("Mary Jane")
+  end
+
+  step 'I click on the "Remove User From Group" button for "John Doe"' do
+    find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click
+    # poltergeist always confirms popups.
+  end
+
+  step 'I click on the "Remove User From Group" button for "Mary Jane"' do
+    find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click
+    # poltergeist always confirms popups.
+  end
+
+  step 'I should not see the "Remove User From Group" button for "John Doe"' do
+    expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove')
+    # poltergeist always confirms popups.
+  end
+
+  step 'I should not see the "Remove User From Group" button for "Mary Jane"' do
+    expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove')
+    # poltergeist always confirms popups.
+  end
+
+  step 'I search for \'Mary\' member' do
+    page.within '.member-search-form' do
+      fill_in 'search', with: 'Mary'
+      click_button 'Search'
+    end
+  end
+
+  step 'I change the "Mary Jane" role to "Developer"' do
+    member = mary_jane_member
+
+    page.within "#group_member_#{member.id}" do
+      find(".js-toggle-button").click
+      page.within "#edit_group_member_#{member.id}" do
+        select 'Developer', from: 'group_member_access_level'
+        click_on 'Save'
+      end
+    end
+  end
+
+  step 'I should see "Mary Jane" as "Developer"' do
+    member = mary_jane_member
+
+    page.within "#group_member_#{member.id}" do
+      page.within '.member-access-level' do
+        expect(page).to have_content "Developer"
+      end
+    end
+  end
+
+  private
+
+  def mary_jane_member
+    user = User.find_by(name: "Mary Jane")
+    owned_group.members.find_by(user_id: user.id)
+  end
+
+  def group_members_list
+    find(".panel .content-list")
+  end
+end
diff --git a/features/steps/group/milestones.rb b/features/steps/group/milestones.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6e57b16ccb69abde504d8b5a12b5811a34d2187a
--- /dev/null
+++ b/features/steps/group/milestones.rb
@@ -0,0 +1,90 @@
+class Spinach::Features::GroupMilestones < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedPaths
+  include SharedGroup
+  include SharedUser
+
+  step 'I click on group milestones' do
+    click_link 'Milestones'
+  end
+
+  step 'I should see group milestones index page has no milestones' do
+    expect(page).to have_content('No milestones to show')
+  end
+
+  step 'Group has projects with milestones' do
+    group_milestone
+  end
+
+  step 'I should see group milestones index page with milestones' do
+    expect(page).to have_content('Version 7.2')
+    expect(page).to have_content('GL-113')
+    expect(page).to have_link('3 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2"))
+    expect(page).to have_link('0 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113"))
+  end
+
+  step 'I click on one group milestone' do
+    click_link 'GL-113'
+  end
+
+  step 'I should see group milestone with descriptions and expiry date' do
+    expect(page).to have_content('expires at Aug 20, 2114')
+  end
+
+  step 'I should see group milestone with all issues and MRs assigned to that milestone' do
+    expect(page).to have_content('Milestone GL-113')
+    expect(page).to have_content('Progress: 0 closed 鈥� 3 open')
+    issue = Milestone.find_by(name: 'GL-113').issues.first
+    expect(page).to have_link(issue.title, href: namespace_project_issue_path(issue.project.namespace, issue.project, issue))
+  end
+
+  step 'I fill milestone name' do
+    fill_in 'milestone_title', with: 'v2.9.0'
+  end
+
+  step 'I click new milestone button' do
+    click_link "New Milestone"
+  end
+
+  step 'I press create mileston button' do
+    click_button "Create Milestone"
+  end
+
+  step 'milestone in each project should be created' do
+    group = Group.find_by(name: 'Owned')
+    expect(page).to have_content "Milestone v2.9.0"
+    expect(group.projects).to be_present
+
+    group.projects.each do |project|
+      expect(page).to have_content project.name
+    end
+  end
+
+  private
+
+  def group_milestone
+    group = owned_group
+
+    %w(gitlabhq gitlab-ci cookbook-gitlab).each do |path|
+      project = create :project, path: path, group: group
+      milestone = create :milestone, title: "Version 7.2", project: project
+      create :issue,
+        project: project,
+        assignee: current_user,
+        author: current_user,
+        milestone: milestone
+
+      milestone = create :milestone,
+        title: "GL-113",
+        project: project,
+        due_date: '2114-08-20',
+        description: 'Lorem Ipsum is simply dummy text'
+
+      create :issue,
+        project: project,
+        assignee: current_user,
+        author: current_user,
+        milestone: milestone
+    end
+  end
+end
diff --git a/features/steps/groups.rb b/features/steps/groups.rb
index 70388c18fcf637e31861d7919a79e9bfdcfc5053..f5e3fee61c0f08fcd26677b5a0c20106b25bb91a 100644
--- a/features/steps/groups.rb
+++ b/features/steps/groups.rb
@@ -3,20 +3,11 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   include SharedPaths
   include SharedGroup
   include SharedUser
-  include Select2Helper
 
   step 'I should see back to dashboard button' do
     expect(page).to have_content 'Go to dashboard'
   end
 
-  step 'gitlab user "Mike"' do
-    create(:user, name: "Mike")
-  end
-
-  step 'I click link "Add members"' do
-    find(:css, 'button.btn-new').click
-  end
-
   step 'I should see group "Owned"' do
     expect(page).to have_content '@owned'
   end
@@ -26,7 +17,7 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   end
 
   step 'Group "Owned" has a public project "Public-project"' do
-    group = Group.find_by(name: "Owned")
+    group = owned_group
 
     @project = create :empty_project, :public,
                  group: group,
@@ -37,43 +28,8 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
     expect(page).to have_content 'Public-project'
   end
 
-  step 'I select "Mike" as "Reporter"' do
-    user = User.find_by(name: "Mike")
-
-    page.within ".users-group-form" do
-      select2(user.id, from: "#user_ids", multiple: true)
-      select "Reporter", from: "access_level"
-    end
-
-    click_button "Add users to group"
-  end
-
-  step 'I should see "Mike" in team list as "Reporter"' do
-    page.within '.well-list' do
-      expect(page).to have_content('Mike')
-      expect(page).to have_content('Reporter')
-    end
-  end
-
-  step 'I select "sjobs@apple.com" as "Reporter"' do
-    page.within ".users-group-form" do
-      select2("sjobs@apple.com", from: "#user_ids", multiple: true)
-      select "Reporter", from: "access_level"
-    end
-
-    click_button "Add users to group"
-  end
-
-  step 'I should see "sjobs@apple.com" in team list as invited "Reporter"' do
-    page.within '.well-list' do
-      expect(page).to have_content('sjobs@apple.com')
-      expect(page).to have_content('invited')
-      expect(page).to have_content('Reporter')
-    end
-  end
-
   step 'I should see group "Owned" projects list' do
-    Group.find_by(name: "Owned").projects.each do |project|
+    owned_group.projects.each do |project|
       expect(page).to have_link project.name
     end
   end
@@ -94,36 +50,6 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
     end
   end
 
-  step 'I select user "Mary Jane" from list with role "Reporter"' do
-    user = User.find_by(name: "Mary Jane") || create(:user, name: "Mary Jane")
-    click_button 'Add members'
-    page.within ".users-group-form" do
-      select2(user.id, from: "#user_ids", multiple: true)
-      select "Reporter", from: "access_level"
-    end
-    click_button "Add users to group"
-  end
-
-  step 'I should see user "John Doe" in team list' do
-    projects_with_access = find(".panel .well-list")
-    expect(projects_with_access).to have_content("John Doe")
-  end
-
-  step 'I should not see user "John Doe" in team list' do
-    projects_with_access = find(".panel .well-list")
-    expect(projects_with_access).not_to have_content("John Doe")
-  end
-
-  step 'I should see user "Mary Jane" in team list' do
-    projects_with_access = find(".panel .well-list")
-    expect(projects_with_access).to have_content("Mary Jane")
-  end
-
-  step 'I should not see user "Mary Jane" in team list' do
-    projects_with_access = find(".panel .well-list")
-    expect(projects_with_access).not_to have_content("Mary Jane")
-  end
-
   step 'project from group "Owned" has issues assigned to me' do
     create :issue,
       project: project,
@@ -154,12 +80,12 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   step 'I change group "Owned" avatar' do
     attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
     click_button "Save group"
-    Group.find_by(name: "Owned").reload
+    owned_group.reload
   end
 
   step 'I should see new group "Owned" avatar' do
-    expect(Group.find_by(name: "Owned").avatar).to be_instance_of AvatarUploader
-    expect(Group.find_by(name: "Owned").avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif"
+    expect(owned_group.avatar).to be_instance_of AvatarUploader
+    expect(owned_group.avatar.url).to eq "/uploads/group/avatar/#{ Group.find_by(name:"Owned").id }/banana_sample.gif"
   end
 
   step 'I should see the "Remove avatar" button' do
@@ -169,83 +95,22 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
   step 'I have group "Owned" avatar' do
     attach_file(:group_avatar, File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif'))
     click_button "Save group"
-    Group.find_by(name: "Owned").reload
+    owned_group.reload
   end
 
   step 'I remove group "Owned" avatar' do
     click_link "Remove avatar"
-    Group.find_by(name: "Owned").reload
+    owned_group.reload
   end
 
   step 'I should not see group "Owned" avatar' do
-    expect(Group.find_by(name: "Owned").avatar?).to eq false
+    expect(owned_group.avatar?).to eq false
   end
 
   step 'I should not see the "Remove avatar" button' do
     expect(page).not_to have_link("Remove avatar")
   end
 
-  step 'I click on the "Remove User From Group" button for "John Doe"' do
-    find(:css, 'li', text: "John Doe").find(:css, 'a.btn-remove').click
-    # poltergeist always confirms popups.
-  end
-
-  step 'I click on the "Remove User From Group" button for "Mary Jane"' do
-    find(:css, 'li', text: "Mary Jane").find(:css, 'a.btn-remove').click
-    # poltergeist always confirms popups.
-  end
-
-  step 'I should not see the "Remove User From Group" button for "John Doe"' do
-    expect(find(:css, 'li', text: "John Doe")).not_to have_selector(:css, 'a.btn-remove')
-    # poltergeist always confirms popups.
-  end
-
-  step 'I should not see the "Remove User From Group" button for "Mary Jane"' do
-    expect(find(:css, 'li', text: "Mary Jane")).not_to have_selector(:css, 'a.btn-remove')
-    # poltergeist always confirms popups.
-  end
-
-  step 'I search for \'Mary\' member' do
-    page.within '.member-search-form' do
-      fill_in 'search', with: 'Mary'
-      click_button 'Search'
-    end
-  end
-
-  step 'I click on group milestones' do
-    click_link 'Milestones'
-  end
-
-  step 'I should see group milestones index page has no milestones' do
-    expect(page).to have_content('No milestones to show')
-  end
-
-  step 'Group has projects with milestones' do
-    group_milestone
-  end
-
-  step 'I should see group milestones index page with milestones' do
-    expect(page).to have_content('Version 7.2')
-    expect(page).to have_content('GL-113')
-    expect(page).to have_link('2 Issues', href: issues_group_path("owned", milestone_title: "Version 7.2"))
-    expect(page).to have_link('3 Merge Requests', href: merge_requests_group_path("owned", milestone_title: "GL-113"))
-  end
-
-  step 'I click on one group milestone' do
-    click_link 'GL-113'
-  end
-
-  step 'I should see group milestone with descriptions and expiry date' do
-    expect(page).to have_content('expires at Aug 20, 2114')
-  end
-
-  step 'I should see group milestone with all issues and MRs assigned to that milestone' do
-    expect(page).to have_content('Milestone GL-113')
-    expect(page).to have_content('Progress: 0 closed 鈥� 4 open')
-    expect(page).to have_link(@issue1.title, href: namespace_project_issue_path(@project1.namespace, @project1, @issue1))
-    expect(page).to have_link(@mr3.title, href: namespace_project_merge_request_path(@project3.namespace, @project3, @mr3))
-  end
-
   step 'Group "Owned" has archived project' do
     group = Group.find_by(name: 'Owned')
     create(:project, namespace: group, archived: true, path: "archived-project")
@@ -255,79 +120,13 @@ class Spinach::Features::Groups < Spinach::FeatureSteps
     expect(page).to have_xpath("//span[@class='label label-warning']", text: 'archived')
   end
 
-  protected
+  private
 
   def assigned_to_me(key)
     project.send(key).where(assignee_id: current_user.id)
   end
 
   def project
-    Group.find_by(name: "Owned").projects.first
-  end
-
-  def group_milestone
-    group = Group.find_by(name: "Owned")
-
-    @project1 = create :project,
-                 group: group
-    project2 = create :project,
-                 path: 'gitlab-ci',
-                 group: group
-    @project3 = create :project,
-                 path: 'cookbook-gitlab',
-                 group: group
-    milestone1_project1 = create :milestone,
-                            title: "Version 7.2",
-                            project: @project1
-    milestone1_project2 = create :milestone,
-                            title: "Version 7.2",
-                            project: project2
-    create :milestone,
-      title: "Version 7.2",
-      project: @project3
-    milestone2_project1 = create :milestone,
-                            title: "GL-113",
-                            project: @project1
-    milestone2_project2 = create :milestone,
-                            title: "GL-113",
-                            project: project2
-    milestone2_project3 = create :milestone,
-                            title: "GL-113",
-                            project: @project3,
-                            due_date: '2114-08-20',
-                            description: 'Lorem Ipsum is simply dummy text of the printing and typesetting industry'
-    @issue1 = create :issue,
-               project: @project1,
-               assignee: current_user,
-               author: current_user,
-               milestone: milestone2_project1
-    create :issue,
-      project: project2,
-      assignee: current_user,
-      author: current_user,
-      milestone: milestone1_project2
-    create :issue,
-      project: @project3,
-      assignee: current_user,
-      author: current_user,
-      milestone: milestone1_project1
-    create :merge_request,
-      source_project: @project1,
-      target_project: @project1,
-      assignee: current_user,
-      author: current_user,
-      milestone: milestone2_project1
-    create :merge_request,
-      source_project: project2,
-      target_project: project2,
-      assignee: current_user,
-      author: current_user,
-      milestone: milestone2_project2
-    @mr3 = create :merge_request,
-            source_project: @project3,
-            target_project: @project3,
-            assignee: current_user,
-            author: current_user,
-            milestone: milestone2_project3
+    owned_group.projects.first
   end
 end
diff --git a/features/steps/project/issues/award_emoji.rb b/features/steps/project/issues/award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8f7a45dec0eb0a77137e36264569a84c6fd11128
--- /dev/null
+++ b/features/steps/project/issues/award_emoji.rb
@@ -0,0 +1,41 @@
+class Spinach::Features::AwardEmoji < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedProject
+  include SharedPaths
+  include Select2Helper
+
+  step 'I visit "Bugfix" issue page' do
+    visit namespace_project_issue_path(@project.namespace, @project, @issue)
+  end
+
+  step 'I click to emoji-picker' do
+    page.within ".awards-controls" do
+      page.find(".add-award").click
+    end
+  end
+
+  step 'I click to emoji in the picker' do
+    page.within ".awards-menu" do
+      page.first("img").click
+    end
+  end
+
+  step 'I can remove it by clicking to icon' do
+    page.within ".awards" do
+      page.first(".award").click
+      expect(page).to_not have_selector ".award"
+    end
+  end
+
+  step 'I have award added' do
+    page.within ".awards" do
+      expect(page).to have_selector ".award"
+      expect(page.find(".award .counter")).to have_content "1"
+    end
+  end
+
+  step 'project "Shop" has issue "Bugfix"' do
+    @project = Project.find_by(name: "Shop")
+    @issue = create(:issue, title: "Bugfix", project: project)
+  end
+end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index af2da41badb220f0fa07e965fc9d079242a43d96..9683bebd5635219a78346fcc876d8bf14d9182ff 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -65,7 +65,7 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
   step 'I see current user as the first user' do
     expect(page).to have_selector('.user-result', visible: true, count: 4)
     users = page.all('.user-name')
-    expect(users[0].text).to eq 'Any'
+    expect(users[0].text).to eq 'Any Assignee'
     expect(users[1].text).to eq 'Unassigned'
     expect(users[2].text).to eq current_user.name
   end
diff --git a/features/steps/project/issues/labels.rb b/features/steps/project/issues/labels.rb
index d656acf4220c1f8d20b0bde8c90f2d28e37f0349..047cf701bb052918e0975448c7f0c73dda0bc516 100644
--- a/features/steps/project/issues/labels.rb
+++ b/features/steps/project/issues/labels.rb
@@ -9,7 +9,7 @@ class Spinach::Features::ProjectIssuesLabels < Spinach::FeatureSteps
 
   step 'I remove label \'bug\'' do
     page.within "#label_#{bug_label.id}" do
-      click_link 'Remove'
+      click_link 'Delete'
     end
   end
 
diff --git a/features/steps/project/issues/milestones.rb b/features/steps/project/issues/milestones.rb
index c8708572ec69af4be20a4e52f203ac02809496d9..e2eda511497f4d6e4bb7974e41f94b8046d04768 100644
--- a/features/steps/project/issues/milestones.rb
+++ b/features/steps/project/issues/milestones.rb
@@ -63,7 +63,7 @@ class Spinach::Features::ProjectIssuesMilestones < Spinach::FeatureSteps
   end
 
   step 'I click link to remove milestone' do
-    click_link 'Remove'
+    click_link 'Delete'
   end
 
   step 'I should see no milestones' do
diff --git a/features/steps/project/source/browse_files.rb b/features/steps/project/source/browse_files.rb
index 84725b9b585444759177ae7383729c808f5f9c23..ceab23b9a4de55a3dad004a740b54d1bc4cca25a 100644
--- a/features/steps/project/source/browse_files.rb
+++ b/features/steps/project/source/browse_files.rb
@@ -87,7 +87,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I click link "Diff"' do
-    click_link 'Preview changes'
+    click_link 'Preview Changes'
   end
 
   step 'I click on "Commit Changes"' do
@@ -98,12 +98,12 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     click_button 'Create directory'
   end
 
-  step 'I click on "Remove"' do
-    click_button 'Remove'
+  step 'I click on "Delete"' do
+    click_button 'Delete'
   end
 
-  step 'I click on "Remove file"' do
-    click_button 'Remove file'
+  step 'I click on "Delete file"' do
+    click_button 'Delete file'
   end
 
   step 'I click on "Replace"' do
@@ -142,7 +142,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I can see new file page' do
-    expect(page).to have_content "new file"
+    expect(page).to have_content "Create New File"
     expect(page).to have_content "Commit message"
   end
 
@@ -192,7 +192,7 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
   end
 
   step 'I see Browse dir link' do
-    expect(page).to have_link 'Browse Dir 禄'
+    expect(page).to have_link 'Browse Directory 禄'
     expect(page).not_to have_link 'Browse Code 禄'
   end
 
@@ -204,13 +204,13 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
 
   step 'I see Browse file link' do
     expect(page).to have_link 'Browse File 禄'
-    expect(page).not_to have_link 'Browse Code 禄'
+    expect(page).not_to have_link 'Browse Files 禄'
   end
 
   step 'I see Browse code link' do
-    expect(page).to have_link 'Browse Code 禄'
+    expect(page).to have_link 'Browse Files 禄'
     expect(page).not_to have_link 'Browse File 禄'
-    expect(page).not_to have_link 'Browse Dir 禄'
+    expect(page).not_to have_link 'Browse Directory 禄'
   end
 
   step 'I click on Permalink' do
@@ -225,10 +225,6 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
     expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'master/.gitignore'))
   end
 
-  step 'I am redirected to the ".gitignore" on new branch' do
-    expect(current_path).to eq(namespace_project_blob_path(@project.namespace, @project, 'new_branch_name/.gitignore'))
-  end
-
   step 'I am redirected to the permalink URL' do
     expect(current_path).to(
       eq(namespace_project_blob_path(@project.namespace, @project,
@@ -247,20 +243,8 @@ class Spinach::Features::ProjectSourceBrowseFiles < Spinach::FeatureSteps
       @project.namespace, @project, 'master/' + new_file_name_with_directory))
   end
 
-  step 'I am redirected to the new file on new branch' do
-    expect(current_path).to eq(namespace_project_blob_path(
-      @project.namespace, @project, 'new_branch_name/' + new_file_name))
-  end
-
-  step 'I am redirected to the uploaded file on new branch' do
-    expect(current_path).to eq(namespace_project_blob_path(
-      @project.namespace, @project,
-      'new_branch_name/' + File.basename(test_text_file)))
-  end
-
-  step 'I am redirected to the new directory' do
-    expect(current_path).to eq(namespace_project_tree_path(
-      @project.namespace, @project, 'new_branch_name/' + new_dir_name))
+  step 'I am redirected to the new merge request page' do
+    expect(current_path).to eq(new_namespace_project_merge_request_path(@project.namespace, @project))
   end
 
   step 'I am redirected to the root directory' do
diff --git a/features/steps/project/source/markdown_render.rb b/features/steps/project/source/markdown_render.rb
index c78e86fa1a731904d81f70512773c7f50cf8e4a5..3a4f7a6e01c4ebd24866afa7486a129550ebe717 100644
--- a/features/steps/project/source/markdown_render.rb
+++ b/features/steps/project/source/markdown_render.rb
@@ -238,7 +238,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
 
   step 'I see new wiki page named test' do
     expect(current_path).to eq  namespace_project_wiki_path(@project.namespace, @project, "test")
-    expect(page).to have_content "Editing"
+    expect(page).to have_content "Edit Page test"
   end
 
   When 'I go back to wiki page home' do
@@ -252,7 +252,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
 
   step 'I see Gitlab API document' do
     expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "api")
-    expect(page).to have_content "Editing"
+    expect(page).to have_content "Edit Page api"
   end
 
   step 'I click on Rake tasks link' do
@@ -261,7 +261,7 @@ class Spinach::Features::ProjectSourceMarkdownRender < Spinach::FeatureSteps
 
   step 'I see Rake tasks directory' do
     expect(current_path).to eq namespace_project_wiki_path(@project.namespace, @project, "raketasks")
-    expect(page).to have_content "Editing"
+    expect(page).to have_content "Edit Page raketasks"
   end
 
   step 'I go directory which contains README file' do
diff --git a/features/steps/project/team_management.rb b/features/steps/project/team_management.rb
index 97d63016458b41b0317108d13bb5423a29017311..caad52def794186ea37cabb59e7e4499dbc86d46 100644
--- a/features/steps/project/team_management.rb
+++ b/features/steps/project/team_management.rb
@@ -15,10 +15,6 @@ class Spinach::Features::ProjectTeamManagement < Spinach::FeatureSteps
     expect(page).to have_content(user.username)
   end
 
-  step 'I click link "Add members"' do
-    find(:css, 'button.btn-new').click
-  end
-
   step 'I select "Mike" as "Reporter"' do
     user = User.find_by(name: "Mike")
 
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 02207dbffa6dcfbd493bc1e16b68bab989dbcc9c..91d227fadbf6b440851e7a0f767bd091881c9b02 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -5,7 +5,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   include SharedPaths
 
   step 'I click on the Cancel button' do
-    page.within(:css, ".form-actions") do
+    page.within(:css, ".wiki-form .form-actions") do
       click_on "Cancel"
     end
   end
@@ -24,7 +24,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
     expect(page).to have_content "link test"
 
     click_link "link test"
-    expect(page).to have_content "Editing"
+    expect(page).to have_content "Edit Page"
   end
 
   step 'I have an existing Wiki page' do
@@ -68,7 +68,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   end
 
   step 'I click on the "Delete this page" button' do
-    click_on "Delete this page"
+    click_on "Delete"
   end
 
   step 'The page should be deleted' do
@@ -120,13 +120,13 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   step 'I should see the new wiki page form' do
     expect(current_path).to match('wikis/image.jpg')
     expect(page).to have_content('New Wiki Page')
-    expect(page).to have_content('Editing - image.jpg')
+    expect(page).to have_content('Edit Page image.jpg')
   end
 
   step 'I create a New page with paths' do
     click_on 'New Page'
     fill_in 'Page slug', with: 'one/two/three'
-    click_on 'Build'
+    click_on 'Create Page'
     fill_in "wiki_content", with: 'wiki content'
     click_on "Create page"
     expect(current_path).to include 'one/two/three'
@@ -135,7 +135,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   step 'I create a New page with an invalid name' do
     click_on 'New Page'
     fill_in 'Page slug', with: 'invalid name'
-    click_on 'Build'
+    click_on 'Create Page'
   end
 
   step 'I should see an error message' do
@@ -156,7 +156,7 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
   end
 
   step 'I should see the Editing page' do
-    expect(page).to have_content('Editing')
+    expect(page).to have_content('Edit Page')
   end
 
   step 'I view the page history of a Wiki page that has a path' do
diff --git a/features/steps/shared/group.rb b/features/steps/shared/group.rb
index 83a04576973d9e2e7f7602e26bb2b0f45c364d03..58581653f281ca8830c5ad0c58d9f8130fc36ae4 100644
--- a/features/steps/shared/group.rb
+++ b/features/steps/shared/group.rb
@@ -41,4 +41,8 @@ module SharedGroup
     project.team << [user, :master]
     @project_count += 1
   end
+
+  def owned_group
+    @owned_group ||= Group.find_by(name: "Owned")
+  end
 end
diff --git a/features/steps/shared/paths.rb b/features/steps/shared/paths.rb
index eb978620da62f20d7f046f17945c8ea51fdaa467..c74a5fd3bc7e7cfa7d044ccb3182622847b0baa6 100644
--- a/features/steps/shared/paths.rb
+++ b/features/steps/shared/paths.rb
@@ -31,6 +31,10 @@ module SharedPaths
     visit merge_requests_group_path(Group.find_by(name: "Owned"))
   end
 
+  step 'I visit group "Owned" milestones page' do
+    visit group_milestones_path(Group.find_by(name: "Owned"))
+  end
+
   step 'I visit group "Owned" members page' do
     visit group_group_members_path(Group.find_by(name: "Owned"))
   end
diff --git a/features/steps/shared/user.rb b/features/steps/shared/user.rb
index fc1e8d6e88967cdef197ecd74de8d64d59a256a6..250cc5b94f3ce039cb3ffb19657ee0f4eadbf696 100644
--- a/features/steps/shared/user.rb
+++ b/features/steps/shared/user.rb
@@ -9,6 +9,10 @@ module SharedUser
     user_exists("Mary Jane", { username: "mary_jane" })
   end
 
+  step 'gitlab user "Mike"' do
+    create(:user, name: "Mike")
+  end
+
   protected
 
   def user_exists(name, options = {})
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 40671e2517c16c97fa627c437eb72dd12dbe4d7e..fe1bf8a48160c1fd4fe244925d227e8ae9db9597 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -52,5 +52,6 @@ module API
     mount Labels
     mount Settings
     mount Keys
+    mount Tags
   end
 end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 20cadae2291f7ebb52fbc6818fd6de7ba5999290..9f337bc3cc6692e1cc3bd6e35f1827c11fae0162 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -62,7 +62,7 @@ module API
       expose :owner, using: Entities::UserBasic, unless: ->(project, options) { project.group }
       expose :name, :name_with_namespace
       expose :path, :path_with_namespace
-      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :snippets_enabled, :created_at, :last_activity_at
+      expose :issues_enabled, :merge_requests_enabled, :wiki_enabled, :builds_enabled, :snippets_enabled, :created_at, :last_activity_at
       expose :creator_id
       expose :namespace
       expose :forked_from_project, using: Entities::ForkedFromProject, if: lambda{ | project, options | project.forked? }
@@ -95,25 +95,6 @@ module API
       end
     end
 
-    class RepoTag < Grape::Entity
-      expose :name
-      expose :message do |repo_obj, _options|
-        if repo_obj.respond_to?(:message)
-          repo_obj.message
-        else
-          nil
-        end
-      end
-
-      expose :commit do |repo_obj, options|
-        if repo_obj.respond_to?(:commit)
-          repo_obj.commit
-        elsif options[:project]
-          options[:project].repository.commit(repo_obj.target)
-        end
-      end
-    end
-
     class RepoObject < Grape::Entity
       expose :name
 
@@ -181,7 +162,9 @@ module API
     end
 
     class MergeRequest < ProjectEntity
-      expose :target_branch, :source_branch, :upvotes, :downvotes
+      expose :target_branch, :source_branch
+      # deprecated, always returns 0
+      expose :upvotes,  :downvotes
       expose :author, :assignee, using: Entities::UserBasic
       expose :source_project_id, :target_project_id
       expose :label_names, as: :labels
@@ -211,6 +194,7 @@ module API
       expose :author, using: Entities::UserBasic
       expose :created_at
       expose :system?, as: :system
+      # upvote? and downvote? are deprecated, always return false
       expose :upvote?, as: :upvote
       expose :downvote?, as: :downvote
     end
@@ -341,5 +325,35 @@ module API
       expose :user_oauth_applications
       expose :after_sign_out_path
     end
+
+    class Release < Grape::Entity
+      expose :tag, as: :tag_name
+      expose :description
+    end
+
+    class RepoTag < Grape::Entity
+      expose :name
+      expose :message do |repo_obj, _options|
+        if repo_obj.respond_to?(:message)
+          repo_obj.message
+        else
+          nil
+        end
+      end
+
+      expose :commit do |repo_obj, options|
+        if repo_obj.respond_to?(:commit)
+          repo_obj.commit
+        elsif options[:project]
+          options[:project].repository.commit(repo_obj.target)
+        end
+      end
+
+      expose :release, using: Entities::Release do |repo_obj, options|
+        if options[:project]
+          options[:project].releases.find_by(tag: repo_obj.name)
+        end
+      end
+    end
   end
 end
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 67ee66a2058a2be885df8cfe1d820a922d9060b5..2b4ada6e2ebbf2e3861128d0788b0adf78284308 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -75,6 +75,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   namespace_id (optional) - defaults to user namespace
@@ -90,6 +91,7 @@ module API
                                      :description,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :namespace_id,
@@ -117,6 +119,7 @@ module API
       #   default_branch (optional) - 'master' by default
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -132,6 +135,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
@@ -172,6 +176,7 @@ module API
       #   description (optional) - short project description
       #   issues_enabled (optional)
       #   merge_requests_enabled (optional)
+      #   builds_enabled (optional)
       #   wiki_enabled (optional)
       #   snippets_enabled (optional)
       #   public (optional) - if true same as setting visibility_level = 20
@@ -185,6 +190,7 @@ module API
                                      :default_branch,
                                      :issues_enabled,
                                      :merge_requests_enabled,
+                                     :builds_enabled,
                                      :wiki_enabled,
                                      :snippets_enabled,
                                      :public,
diff --git a/lib/api/repositories.rb b/lib/api/repositories.rb
index 20d568cf4626ed9b7b292a11c066c30b0daccc6e..d7c48639eba42d1b195c2b2121ef401d3ca040ce 100644
--- a/lib/api/repositories.rb
+++ b/lib/api/repositories.rb
@@ -16,41 +16,6 @@ module API
         end
       end
 
-      # Get a project repository tags
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      # Example Request:
-      #   GET /projects/:id/repository/tags
-      get ":id/repository/tags" do
-        present user_project.repo.tags.sort_by(&:name).reverse,
-                with: Entities::RepoTag, project: user_project
-      end
-
-      # Create tag
-      #
-      # Parameters:
-      #   id (required) - The ID of a project
-      #   tag_name (required) - The name of the tag
-      #   ref (required) - Create tag from commit sha or branch
-      #   message (optional) - Specifying a message creates an annotated tag.
-      # Example Request:
-      #   POST /projects/:id/repository/tags
-      post ':id/repository/tags' do
-        authorize_push_project
-        message = params[:message] || nil
-        result = CreateTagService.new(user_project, current_user).
-          execute(params[:tag_name], params[:ref], message)
-
-        if result[:status] == :success
-          present result[:tag],
-                  with: Entities::RepoTag,
-                  project: user_project
-        else
-          render_api_error!(result[:message], 400)
-        end
-      end
-
       # Get a project repository tree
       #
       # Parameters:
diff --git a/lib/api/tags.rb b/lib/api/tags.rb
new file mode 100644
index 0000000000000000000000000000000000000000..47621f443e6c088ffd66ff804c497353314d8a28
--- /dev/null
+++ b/lib/api/tags.rb
@@ -0,0 +1,86 @@
+module API
+  # Git Tags API
+  class Tags < Grape::API
+    before { authenticate! }
+    before { authorize! :download_code, user_project }
+
+    resource :projects do
+      # Get a project repository tags
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      # Example Request:
+      #   GET /projects/:id/repository/tags
+      get ":id/repository/tags" do
+        present user_project.repo.tags.sort_by(&:name).reverse,
+                with: Entities::RepoTag, project: user_project
+      end
+
+      # Create tag
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   tag_name (required) - The name of the tag
+      #   ref (required) - Create tag from commit sha or branch
+      #   message (optional) - Specifying a message creates an annotated tag.
+      # Example Request:
+      #   POST /projects/:id/repository/tags
+      post ':id/repository/tags' do
+        authorize_push_project
+        message = params[:message] || nil
+        result = CreateTagService.new(user_project, current_user).
+          execute(params[:tag_name], params[:ref], message, params[:release_description])
+
+        if result[:status] == :success
+          present result[:tag],
+                  with: Entities::RepoTag,
+                  project: user_project
+        else
+          render_api_error!(result[:message], 400)
+        end
+      end
+
+      # Add release notes to tag
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   tag_name (required) - The name of the tag
+      #   description (required) - Release notes with markdown support
+      # Example Request:
+      #   POST /projects/:id/repository/tags/:tag_name/release
+      post ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do
+        authorize_push_project
+        required_attributes! [:description]
+        result = CreateReleaseService.new(user_project, current_user).
+          execute(params[:tag_name], params[:description])
+
+        if result[:status] == :success
+          present result[:release], with: Entities::Release
+        else
+          render_api_error!(result[:message], result[:http_status])
+        end
+      end
+
+      # Updates a release notes of a tag
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   tag_name (required) - The name of the tag
+      #   description (required) - Release notes with markdown support
+      # Example Request:
+      #   PUT /projects/:id/repository/tags/:tag_name/release
+      put ':id/repository/tags/:tag_name/release', requirements: { tag_name: /.*/ } do
+        authorize_push_project
+        required_attributes! [:description]
+        result = UpdateReleaseService.new(user_project, current_user).
+          execute(params[:tag_name], params[:description])
+
+        if result[:status] == :success
+          present result[:release], with: Entities::Release
+        else
+          render_api_error!(result[:message], result[:http_status])
+        end
+      end
+    end
+  end
+end
diff --git a/lib/award_emoji.rb b/lib/award_emoji.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d58a196c4efda38b8875ea78256072b0818a2a97
--- /dev/null
+++ b/lib/award_emoji.rb
@@ -0,0 +1,12 @@
+class AwardEmoji
+  EMOJI_LIST = [
+    "+1", "-1", "100", "blush", "heart", "smile", "rage",
+    "beers", "disappointed", "ok_hand",
+    "helicopter", "shit", "airplane", "alarm_clock",
+    "ambulance", "anguished", "two_hearts", "wink"
+  ]
+
+  def self.path_to_emoji_image(name)
+    "emoji/#{Emoji.emoji_filename(name)}.png"
+  end
+end
diff --git a/lib/backup/lfs.rb b/lib/backup/lfs.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4153467fbeeea29b981762f667939aa0440ed5b2
--- /dev/null
+++ b/lib/backup/lfs.rb
@@ -0,0 +1,13 @@
+require 'backup/files'
+
+module Backup
+  class Lfs < Files
+    def initialize
+      super('lfs', Settings.lfs.storage_path)
+    end
+
+    def create_files_dir
+      Dir.mkdir(app_files_dir, 0700)
+    end
+  end
+end
diff --git a/lib/backup/manager.rb b/lib/backup/manager.rb
index 9e15d5411a1742760434937936ddd2fa1621c99b..099062eeb8b885c5057aba520d575b8789c62995 100644
--- a/lib/backup/manager.rb
+++ b/lib/backup/manager.rb
@@ -150,17 +150,15 @@ module Backup
     private
 
     def backup_contents
-      folders_to_backup + ["uploads.tar.gz", "builds.tar.gz", "artifacts.tar.gz", "backup_information.yml"]
+      folders_to_backup + archives_to_backup + ["backup_information.yml"]
     end
 
-    def folders_to_backup
-      folders = %w{repositories db}
-
-      if ENV["SKIP"]
-        return folders.reject{ |folder| ENV["SKIP"].include?(folder) }
-      end
+    def archives_to_backup
+      %w{uploads builds artifacts lfs}.map{ |name| (name + ".tar.gz") unless skipped?(name) }.compact
+    end
 
-      folders
+    def folders_to_backup
+      %w{repositories db}.reject{ |name| skipped?(name) }
     end
 
     def settings
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 0a58667280745de66b4fc6f89488d55fa796516d..15faa6edd8402c2ca4a11a8c7493db453153f1b3 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -58,6 +58,7 @@ module Ci
         #   POST /builds/:id/artifacts/authorize
         post ":id/artifacts/authorize" do
           require_gitlab_workhorse!
+          not_allowed! unless Gitlab.config.artifacts.enabled
           build = Ci::Build.find_by_id(params[:id])
           not_found! unless build
           authenticate_build_token!(build)
@@ -91,6 +92,7 @@ module Ci
         #   POST /builds/:id/artifacts
         post ":id/artifacts" do
           require_gitlab_workhorse!
+          not_allowed! unless Gitlab.config.artifacts.enabled
           build = Ci::Build.find_by_id(params[:id])
           not_found! unless build
           authenticate_build_token!(build)
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index 2e2209031eef28bf7e0d4bf40d1eb0109b4b1c6a..3beafcad117f19cbaafa13f432c632d1b65ed894 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -4,10 +4,10 @@ module Ci
 
     DEFAULT_STAGES = %w(build test deploy)
     DEFAULT_STAGE = 'test'
-    ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables]
-    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts]
+    ALLOWED_YAML_KEYS = [:before_script, :image, :services, :types, :stages, :variables, :cache]
+    ALLOWED_JOB_KEYS = [:tags, :script, :only, :except, :type, :image, :services, :allow_failure, :type, :stage, :when, :artifacts, :cache]
 
-    attr_reader :before_script, :image, :services, :variables, :path
+    attr_reader :before_script, :image, :services, :variables, :path, :cache
 
     def initialize(config, path = nil)
       @config = YAML.load(config)
@@ -46,6 +46,7 @@ module Ci
       @services = @config[:services]
       @stages = @config[:stages] || @config[:types]
       @variables = @config[:variables] || {}
+      @cache = @config[:cache]
       @config.except!(*ALLOWED_YAML_KEYS)
 
       # anything that doesn't have script is considered as unknown
@@ -78,7 +79,8 @@ module Ci
         options: {
           image: job[:image] || @image,
           services: job[:services] || @services,
-          artifacts: job[:artifacts]
+          artifacts: job[:artifacts],
+          cache: job[:cache] || @cache,
         }.compact
       }
     end
@@ -112,6 +114,16 @@ module Ci
         raise ValidationError, "variables should be a map of key-valued strings"
       end
 
+      if @cache
+        if @cache[:untracked] && !validate_boolean(@cache[:untracked])
+          raise ValidationError, "cache:untracked parameter should be an boolean"
+        end
+
+        if @cache[:paths] && !validate_array_of_strings(@cache[:paths])
+          raise ValidationError, "cache:paths parameter should be an array of strings"
+        end
+      end
+
       @jobs.each do |name, job|
         validate_job!(name, job)
       end
@@ -160,6 +172,16 @@ module Ci
         raise ValidationError, "#{name} job: except parameter should be an array of strings"
       end
 
+      if job[:cache]
+        if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
+          raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
+        end
+
+        if job[:cache][:paths] && !validate_array_of_strings(job[:cache][:paths])
+          raise ValidationError, "#{name} job: cache:paths parameter should be an array of strings"
+        end
+      end
+
       if job[:artifacts]
         if job[:artifacts][:untracked] && !validate_boolean(job[:artifacts][:untracked])
           raise ValidationError, "#{name} job: artifacts:untracked parameter should be an boolean"
diff --git a/lib/gitlab/backend/grack_auth.rb b/lib/gitlab/backend/grack_auth.rb
index 440ef5a3cb35d795a56519b7b37b1f7c98cbe5e7..0d156047ff09f7318542db1aaf44525172152cef 100644
--- a/lib/gitlab/backend/grack_auth.rb
+++ b/lib/gitlab/backend/grack_auth.rb
@@ -33,6 +33,9 @@ module Grack
 
       auth!
 
+      lfs_response = Gitlab::Lfs::Router.new(project, @user, @request).try_call
+      return lfs_response unless lfs_response.nil?
+
       if project && authorized_request?
         # Tell gitlab-workhorse the request is OK, and what the GL_ID is
         render_grack_auth_ok
@@ -72,7 +75,7 @@ module Grack
       matched_login = /(?<s>^[a-zA-Z]*-ci)-token$/.match(login)
 
       if project && matched_login.present? && git_cmd == 'git-upload-pack'
-        underscored_service = matched_login['s'].underscore 
+        underscored_service = matched_login['s'].underscore
 
         if Service.available_services_names.include?(underscored_service)
           service_method = "#{underscored_service}_service"
diff --git a/lib/gitlab/backend/shell.rb b/lib/gitlab/backend/shell.rb
index 01b8bda05c6741cf7b914735722447388cec42f7..87ac30b5ffef959e7baa2a60f8e28d41310099ed 100644
--- a/lib/gitlab/backend/shell.rb
+++ b/lib/gitlab/backend/shell.rb
@@ -1,6 +1,6 @@
 module Gitlab
   class Shell
-    class AccessDenied < StandardError; end
+    class Error < StandardError; end
 
     class KeyAdder < Struct.new(:io)
       def add_key(id, key)
@@ -36,8 +36,9 @@ module Gitlab
     #   import_repository("gitlab/gitlab-ci", "https://github.com/randx/six.git")
     #
     def import_repository(name, url)
-      Gitlab::Utils.system_silent([gitlab_shell_projects_path, 'import-project',
-                                   "#{name}.git", url, '240'])
+      output, status = Popen::popen([gitlab_shell_projects_path, 'import-project', "#{name}.git", url, '240'])
+      raise Error, output unless status.zero?
+      true
     end
 
     # Move repository
diff --git a/lib/gitlab/compare_result.rb b/lib/gitlab/compare_result.rb
index d72391dade5f025022bcc4974ca0e1ec8db2751d..0d696a1ee282a298a89aac585b0813e27edca777 100644
--- a/lib/gitlab/compare_result.rb
+++ b/lib/gitlab/compare_result.rb
@@ -2,8 +2,8 @@ module Gitlab
   class CompareResult
     attr_reader :commits, :diffs
 
-    def initialize(compare)
-      @commits, @diffs = compare.commits, compare.diffs
+    def initialize(compare, diff_options = {})
+      @commits, @diffs = compare.commits, compare.diffs(nil, diff_options)
     end
   end
 end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 2d3e32d95396111ba51b887bf49c504090f5d056..46a4ef0e31febfd300cbc35bc5abb88291e59f27 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -25,7 +25,7 @@ module Gitlab
         session_expire_delay: Settings.gitlab['session_expire_delay'],
         import_sources: Settings.gitlab['import_sources'],
         shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'],
-        max_artifacts_size: Ci::Settings.gitlab_ci['max_artifacts_size'],
+        max_artifacts_size: Settings.artifacts['max_size'],
       )
     end
 
diff --git a/lib/gitlab/git/hook.rb b/lib/gitlab/git/hook.rb
index dd393fe09d201e8739e3fc5224061565fb36a844..07b856ca64cffec10fa8922eacf8a337bdca17d7 100644
--- a/lib/gitlab/git/hook.rb
+++ b/lib/gitlab/git/hook.rb
@@ -16,6 +16,17 @@ module Gitlab
       def trigger(gl_id, oldrev, newrev, ref)
         return true unless exists?
 
+        case name
+        when "pre-receive", "post-receive"
+          call_receive_hook(gl_id, oldrev, newrev, ref)
+        when "update"
+          call_update_hook(gl_id, oldrev, newrev, ref)
+        end
+      end
+
+      private
+
+      def call_receive_hook(gl_id, oldrev, newrev, ref)
         changes = [oldrev, newrev, ref].join(" ")
 
         # function  will return true if succesful
@@ -54,6 +65,12 @@ module Gitlab
 
         exit_status
       end
+
+      def call_update_hook(gl_id, oldrev, newrev, ref)
+        Dir.chdir(repo_path) do
+          system({ 'GL_ID' => gl_id }, path, ref, oldrev, newrev)
+        end
+      end
     end
   end
 end
diff --git a/lib/gitlab/git_access.rb b/lib/gitlab/git_access.rb
index c90184d31cf1c3899ff65df28da10ff388b480bc..3ed1eec517c0143b204ae59c793e268ea483a1c0 100644
--- a/lib/gitlab/git_access.rb
+++ b/lib/gitlab/git_access.rb
@@ -13,7 +13,7 @@ module Gitlab
     def user
       return @user if defined?(@user)
 
-      @user = 
+      @user =
         case actor
         when User
           actor
@@ -125,7 +125,7 @@ module Gitlab
     def change_access_check(change)
       oldrev, newrev, ref = change.split(' ')
 
-      action = 
+      action =
         if project.protected_branch?(branch_name(ref))
           protected_branch_action(oldrev, newrev, branch_name(ref))
         elsif protected_tag?(tag_name(ref))
@@ -148,7 +148,7 @@ module Gitlab
             build_status_object(false, "You are not allowed to change existing tags on this project.")
           else # :push_code
             build_status_object(false, "You are not allowed to push code to this project.")
-          end 
+          end
         return status
       end
 
diff --git a/lib/gitlab/github_import/client.rb b/lib/gitlab/github_import/client.rb
index 270cbcd9ccd92b9ec35da9d90ec688b81f05b983..74d1529e1ff55bc3ce20b6d5ba70f84acc4d2ad6 100644
--- a/lib/gitlab/github_import/client.rb
+++ b/lib/gitlab/github_import/client.rb
@@ -46,7 +46,7 @@ module Gitlab
       end
 
       def github_options
-        OmniAuth::Strategies::GitHub.default_options[:client_options].symbolize_keys
+        OmniAuth::Strategies::GitHub.default_options[:client_options].to_h.symbolize_keys
       end
     end
   end
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index bd7340a80f18ca49c6724d8d9cb81aa5577eb128..b5720f6e2cb57f5b66d6a47e91bd315f258354c5 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -19,7 +19,7 @@ module Gitlab
           if issue.pull_request.nil?
 
             body = @formatter.author_line(issue.user.login)
-            body += issue.body
+            body += issue.body || ""
 
             if issue.comments > 0
               body += @formatter.comments_header
diff --git a/lib/gitlab/gitlab_import/client.rb b/lib/gitlab/gitlab_import/client.rb
index 9c00896c913bf080e0aee65bb8d581d2e315b45b..86fb6c51765208fbb8698fdf9d9db5b68f22cad1 100644
--- a/lib/gitlab/gitlab_import/client.rb
+++ b/lib/gitlab/gitlab_import/client.rb
@@ -75,7 +75,7 @@ module Gitlab
       end
 
       def gitlab_options
-        OmniAuth::Strategies::GitLab.default_options[:client_options].symbolize_keys
+        OmniAuth::Strategies::GitLab.default_options[:client_options].to_h.symbolize_keys
       end
     end
   end
diff --git a/lib/gitlab/google_code_import/importer.rb b/lib/gitlab/google_code_import/importer.rb
index 03c410726a577fa98f4d95fd624642bf033b7c8e..87fee28dc010adfcae54c903c427a67d8634b54a 100644
--- a/lib/gitlab/google_code_import/importer.rb
+++ b/lib/gitlab/google_code_import/importer.rb
@@ -30,7 +30,7 @@ module Gitlab
 
       def user_map
         @user_map ||= begin
-          user_map = Hash.new do |hash, user| 
+          user_map = Hash.new do |hash, user|
             # Replace ... by \.\.\., so `johnsm...@gmail.com` isn't autolinked.
             Client.mask_email(user).sub("...", "\\.\\.\\.")
           end
@@ -76,18 +76,7 @@ module Gitlab
           attachments = format_attachments(raw_issue["id"], 0, issue_comment["attachments"])
 
           body = format_issue_body(author, date, content, attachments)
-
-          labels = []
-          raw_issue["labels"].each do |label|
-            name = nice_label_name(label)
-            labels << name
-
-            unless @known_labels.include?(name)
-              create_label(name)
-              @known_labels << name
-            end
-          end
-          labels << nice_status_name(raw_issue["status"])
+          labels = import_issue_labels(raw_issue)
 
           assignee_id = nil
           if raw_issue.has_key?("owner")
@@ -110,6 +99,7 @@ module Gitlab
             assignee_id:  assignee_id,
             state:        raw_issue["state"] == "closed" ? "closed" : "opened"
           )
+
           issue.add_labels_by_names(labels)
 
           if issue.iid != raw_issue["id"]
@@ -120,6 +110,23 @@ module Gitlab
         end
       end
 
+      def import_issue_labels(raw_issue)
+        labels = []
+
+        raw_issue["labels"].each do |label|
+          name = nice_label_name(label)
+          labels << name
+
+          unless @known_labels.include?(name)
+            create_label(name)
+            @known_labels << name
+          end
+        end
+
+        labels << nice_status_name(raw_issue["status"])
+        labels
+      end
+
       def import_issue_comments(issue, comments)
         Note.transaction do
           while raw_comment = comments.shift
@@ -172,7 +179,7 @@ module Gitlab
           "#5cb85c"
         when "Status: Started"
           "#8e44ad"
-        
+
         when "Priority: Critical"
           "#ffcfcf"
         when "Priority: High"
@@ -181,7 +188,7 @@ module Gitlab
           "#fff5cc"
         when "Priority: Low"
           "#cfe9ff"
-        
+
         when "Type: Defect"
           "#d9534f"
         when "Type: Enhancement"
@@ -249,8 +256,8 @@ module Gitlab
         end
 
         if raw_updates.has_key?("cc")
-          cc = raw_updates["cc"].map do |l| 
-            deleted = l.start_with?("-") 
+          cc = raw_updates["cc"].map do |l|
+            deleted = l.start_with?("-")
             l = l[1..-1] if deleted
             l = user_map[l]
             l = "~~#{l}~~" if deleted
@@ -261,8 +268,8 @@ module Gitlab
         end
 
         if raw_updates.has_key?("labels")
-          labels = raw_updates["labels"].map do |l| 
-            deleted = l.start_with?("-") 
+          labels = raw_updates["labels"].map do |l|
+            deleted = l.start_with?("-")
             l = l[1..-1] if deleted
             l = nice_label_name(l)
             l = "~~#{l}~~" if deleted
@@ -278,45 +285,39 @@ module Gitlab
 
         if raw_updates.has_key?("blockedOn")
           blocked_ons = raw_updates["blockedOn"].map do |raw_blocked_on|
-            name, id = raw_blocked_on.split(":", 2)
-
-            deleted = name.start_with?("-") 
-            name = name[1..-1] if deleted
-
-            text =
-              if name == project.import_source
-                "##{id}"
-              else
-                "#{project.namespace.path}/#{name}##{id}"
-              end
-            text = "~~#{text}~~" if deleted
-            text
+            format_blocking_updates(raw_blocked_on)
           end
+
           updates << "*Blocked on: #{blocked_ons.join(", ")}*"
         end
 
         if raw_updates.has_key?("blocking")
           blockings = raw_updates["blocking"].map do |raw_blocked_on|
-            name, id = raw_blocked_on.split(":", 2)
-            
-            deleted = name.start_with?("-") 
-            name = name[1..-1] if deleted
-
-            text =
-              if name == project.import_source
-                "##{id}"
-              else
-                "#{project.namespace.path}/#{name}##{id}"
-              end
-            text = "~~#{text}~~" if deleted
-            text
+            format_blocking_updates(raw_blocked_on)
           end
+
           updates << "*Blocking: #{blockings.join(", ")}*"
         end
 
         updates
       end
 
+      def format_blocking_updates(raw_blocked_on)
+        name, id = raw_blocked_on.split(":", 2)
+
+        deleted = name.start_with?("-")
+        name = name[1..-1] if deleted
+
+        text =
+          if name == project.import_source
+            "##{id}"
+          else
+            "#{project.namespace.path}/#{name}##{id}"
+          end
+        text = "~~#{text}~~" if deleted
+        text
+      end
+
       def format_attachments(issue_id, comment_id, raw_attachments)
         return [] unless raw_attachments
 
@@ -325,7 +326,7 @@ module Gitlab
 
           filename = attachment["fileName"]
           link = "https://storage.googleapis.com/google-code-attachments/#{@repo.name}/issue-#{issue_id}/comment-#{comment_id}/#{filename}"
-          
+
           text = "[#{filename}](#{link})"
           text = "!#{text}" if filename =~ /\.(png|jpg|jpeg|gif|bmp|tiff)\z/i
           text
diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb
index 99e7b529ba9534c6612baea3f110db08520322b2..44507bde25deaef0e8f16adab6ca7af2670c99d7 100644
--- a/lib/gitlab/inline_diff.rb
+++ b/lib/gitlab/inline_diff.rb
@@ -11,48 +11,71 @@ module Gitlab
         indexes.each do |index|
           first_line = diff_arr[index+1]
           second_line = diff_arr[index+2]
-          max_length = [first_line.size, second_line.size].max
 
           # Skip inline diff if empty line was replaced with content
           next if first_line == "-\n"
 
-          first_the_same_symbols = 0
-          (0..max_length + 1).each do |i|
-            first_the_same_symbols = i - 1
-            if first_line[i] != second_line[i] && i > 0
-              break
-            end
-          end
+          first_token = find_first_token(first_line, second_line)
+          apply_first_token(diff_arr, index, first_token)
+
+          last_token = find_last_token(first_line, second_line, first_token)
+          apply_last_token(diff_arr, index, last_token)
+        end
+
+        diff_arr
+      end
+
+      def apply_first_token(diff_arr, index, first_token)
+        start = first_token + START
+
+        if first_token.empty?
+          # In case if we remove string of spaces in commit
+          diff_arr[index+1].sub!("-", "-" => "-#{START}")
+          diff_arr[index+2].sub!("+", "+" => "+#{START}")
+        else
+          diff_arr[index+1].sub!(first_token, first_token => start)
+          diff_arr[index+2].sub!(first_token, first_token => start)
+        end
+      end
 
-          first_token = first_line[0..first_the_same_symbols][1..-1]
-          start = first_token + START
+      def apply_last_token(diff_arr, index, last_token)
+        # This is tricky: escape backslashes so that `sub` doesn't interpret them
+        # as backreferences. Regexp.escape does NOT do the right thing.
+        replace_token = FINISH + last_token.gsub(/\\/, '\&\&')
+        diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
+        diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
+      end
+
+      def find_first_token(first_line, second_line)
+        max_length = [first_line.size, second_line.size].max
+        first_the_same_symbols = 0
+
+        (0..max_length + 1).each do |i|
+          first_the_same_symbols = i - 1
 
-          if first_token.empty?
-            # In case if we remove string of spaces in commit
-            diff_arr[index+1].sub!("-", "-" => "-#{START}")
-            diff_arr[index+2].sub!("+", "+" => "+#{START}")
-          else
-            diff_arr[index+1].sub!(first_token, first_token => start)
-            diff_arr[index+2].sub!(first_token, first_token => start)
+          if first_line[i] != second_line[i] && i > 0
+            break
           end
+        end
+
+        first_line[0..first_the_same_symbols][1..-1]
+      end
+
+      def find_last_token(first_line, second_line, first_token)
+        max_length = [first_line.size, second_line.size].max
+        last_the_same_symbols = 0
+
+        (1..max_length + 1).each do |i|
+          last_the_same_symbols = -i
+          shortest_line = second_line.size > first_line.size ? first_line : second_line
 
-          last_the_same_symbols = 0
-          (1..max_length + 1).each do |i|
-            last_the_same_symbols = -i
-            shortest_line = second_line.size > first_line.size ? first_line : second_line
-            if ( first_line[-i] != second_line[-i] ) || "#{first_token}#{START}".size == shortest_line[1..-i].size
-              break
-            end
+          if (first_line[-i] != second_line[-i]) || "#{first_token}#{START}".size == shortest_line[1..-i].size
+            break
           end
-          last_the_same_symbols += 1
-          last_token = first_line[last_the_same_symbols..-1]
-          # This is tricky: escape backslashes so that `sub` doesn't interpret them
-          # as backreferences. Regexp.escape does NOT do the right thing.
-          replace_token = FINISH + last_token.gsub(/\\/, '\&\&')
-          diff_arr[index+1].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
-          diff_arr[index+2].sub!(/#{Regexp.escape(last_token)}$/, replace_token)
         end
-        diff_arr
+
+        last_the_same_symbols += 1
+        first_line[last_the_same_symbols..-1]
       end
 
       def _indexes_of_changed_lines(diff_arr)
diff --git a/lib/gitlab/lfs/response.rb b/lib/gitlab/lfs/response.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9be9a65671b2b6f37436eb3220220354cd20e4f8
--- /dev/null
+++ b/lib/gitlab/lfs/response.rb
@@ -0,0 +1,327 @@
+module Gitlab
+  module Lfs
+    class Response
+
+      def initialize(project, user, request)
+        @origin_project = project
+        @project = storage_project(project)
+        @user = user
+        @env = request.env
+        @request = request
+      end
+
+      def render_download_object_response(oid)
+        render_response_to_download do
+          if check_download_sendfile_header?
+            render_lfs_sendfile(oid)
+          else
+            render_not_found
+          end
+        end
+      end
+
+      def render_batch_operation_response
+        request_body = JSON.parse(@request.body.read)
+        case request_body["operation"]
+        when "download"
+          render_batch_download(request_body)
+        when "upload"
+          render_batch_upload(request_body)
+        else
+          render_not_found
+        end
+      end
+
+      def render_storage_upload_authorize_response(oid, size)
+        render_response_to_push do
+          [
+            200,
+            { "Content-Type" => "application/json; charset=utf-8" },
+            [JSON.dump({
+              'StoreLFSPath' => "#{Gitlab.config.lfs.storage_path}/tmp/upload",
+              'LfsOid' => oid,
+              'LfsSize' => size
+            })]
+          ]
+        end
+      end
+
+      def render_storage_upload_store_response(oid, size, tmp_file_name)
+        render_response_to_push do
+          render_lfs_upload_ok(oid, size, tmp_file_name)
+        end
+      end
+
+      def render_unsupported_deprecated_api
+        [
+          501,
+          { "Content-Type" => "application/json; charset=utf-8" },
+          [JSON.dump({
+            'message' => 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      private
+
+      def render_not_enabled
+        [
+          501,
+          {
+            "Content-Type" => "application/json; charset=utf-8",
+          },
+          [JSON.dump({
+            'message' => 'Git LFS is not enabled on this GitLab server, contact your admin.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_unauthorized
+        [
+          401,
+          {
+            'Content-Type' => 'text/plain'
+          },
+          ['Unauthorized']
+        ]
+      end
+
+      def render_not_found
+        [
+          404,
+          {
+            "Content-Type" => "application/vnd.git-lfs+json"
+          },
+          [JSON.dump({
+            'message' => 'Not found.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_forbidden
+        [
+          403,
+          {
+            "Content-Type" => "application/vnd.git-lfs+json"
+          },
+          [JSON.dump({
+            'message' => 'Access forbidden. Check your access level.',
+            'documentation_url' => "#{Gitlab.config.gitlab.url}/help",
+          })]
+        ]
+      end
+
+      def render_lfs_sendfile(oid)
+        return render_not_found unless oid.present?
+
+        lfs_object = object_for_download(oid)
+
+        if lfs_object && lfs_object.file.exists?
+          [
+            200,
+            {
+              # GitLab-workhorse will forward Content-Type header
+              "Content-Type" => "application/octet-stream",
+              "X-Sendfile" => lfs_object.file.path
+            },
+            []
+          ]
+        else
+          render_not_found
+        end
+      end
+
+      def render_batch_upload(body)
+        return render_not_found if body.empty? || body['objects'].nil?
+
+        render_response_to_push do
+          response = build_upload_batch_response(body['objects'])
+          [
+            200,
+            {
+              "Content-Type" => "application/json; charset=utf-8",
+              "Cache-Control" => "private",
+            },
+            [JSON.dump(response)]
+          ]
+        end
+      end
+
+      def render_batch_download(body)
+        return render_not_found if body.empty? || body['objects'].nil?
+
+        render_response_to_download do
+          response = build_download_batch_response(body['objects'])
+          [
+            200,
+            {
+              "Content-Type" => "application/json; charset=utf-8",
+              "Cache-Control" => "private",
+            },
+            [JSON.dump(response)]
+          ]
+        end
+      end
+
+      def render_lfs_upload_ok(oid, size, tmp_file)
+        if store_file(oid, size, tmp_file)
+          [
+            200,
+            {
+              'Content-Type' => 'text/plain',
+              'Content-Length' => 0
+            },
+            []
+          ]
+        else
+          [
+            422,
+            { 'Content-Type' => 'text/plain' },
+            ["Unprocessable entity"]
+          ]
+        end
+      end
+
+      def render_response_to_download
+        return render_not_enabled unless Gitlab.config.lfs.enabled
+
+        unless @project.public?
+          return render_unauthorized unless @user
+          return render_forbidden unless user_can_fetch?
+        end
+
+        yield
+      end
+
+      def render_response_to_push
+        return render_not_enabled unless Gitlab.config.lfs.enabled
+        return render_unauthorized unless @user
+        return render_forbidden unless user_can_push?
+
+        yield
+      end
+
+      def check_download_sendfile_header?
+        @env['HTTP_X_SENDFILE_TYPE'].to_s == "X-Sendfile"
+      end
+
+      def user_can_fetch?
+        # Check user access against the project they used to initiate the pull
+        @user.can?(:download_code, @origin_project)
+      end
+
+      def user_can_push?
+        # Check user access against the project they used to initiate the push
+        @user.can?(:push_code, @origin_project)
+      end
+
+      def storage_project(project)
+        if project.forked?
+          project.forked_from_project
+        else
+          project
+        end
+      end
+
+      def store_file(oid, size, tmp_file)
+        tmp_file_path = File.join("#{Gitlab.config.lfs.storage_path}/tmp/upload", tmp_file)
+
+        object = LfsObject.find_or_create_by(oid: oid, size: size)
+        if object.file.exists?
+          success = true
+        else
+          success = move_tmp_file_to_storage(object, tmp_file_path)
+        end
+
+        if success
+          success = link_to_project(object)
+        end
+
+        success
+      ensure
+        # Ensure that the tmp file is removed
+        FileUtils.rm_f(tmp_file_path)
+      end
+
+      def object_for_download(oid)
+        @project.lfs_objects.find_by(oid: oid)
+      end
+
+      def move_tmp_file_to_storage(object, path)
+        File.open(path) do |f|
+          object.file = f
+        end
+
+        object.file.store!
+        object.save
+      end
+
+      def link_to_project(object)
+        if object && !object.projects.exists?(@project.id)
+          object.projects << @project
+          object.save
+        end
+      end
+
+      def select_existing_objects(objects)
+        objects_oids = objects.map { |o| o['oid'] }
+        @project.lfs_objects.where(oid: objects_oids).pluck(:oid).to_set
+      end
+
+      def build_upload_batch_response(objects)
+        selected_objects = select_existing_objects(objects)
+
+        upload_hypermedia_links(objects, selected_objects)
+      end
+
+      def build_download_batch_response(objects)
+        selected_objects = select_existing_objects(objects)
+
+        download_hypermedia_links(objects, selected_objects)
+      end
+
+      def download_hypermedia_links(all_objects, existing_objects)
+        all_objects.each do |object|
+          if existing_objects.include?(object['oid'])
+            object['actions'] = {
+              'download' => {
+                'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}",
+                'header' => {
+                  'Authorization' => @env['HTTP_AUTHORIZATION']
+                }.compact
+              }
+            }
+          else
+            object['error'] = {
+              'code' => 404,
+              'message' => "Object does not exist on the server or you don't have permissions to access it",
+            }
+          end
+        end
+
+        { 'objects' => all_objects }
+      end
+
+      def upload_hypermedia_links(all_objects, existing_objects)
+        all_objects.each do |object|
+          # generate actions only for non-existing objects
+          next if existing_objects.include?(object['oid'])
+
+          object['actions'] = {
+            'upload' => {
+              'href' => "#{@origin_project.http_url_to_repo}/gitlab-lfs/objects/#{object['oid']}/#{object['size']}",
+              'header' => {
+                'Authorization' => @env['HTTP_AUTHORIZATION']
+              }.compact
+            }
+          }
+        end
+
+        { 'objects' => all_objects }
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/lfs/router.rb b/lib/gitlab/lfs/router.rb
new file mode 100644
index 0000000000000000000000000000000000000000..78d028911021612dc90709302f46e02ca535a9d7
--- /dev/null
+++ b/lib/gitlab/lfs/router.rb
@@ -0,0 +1,97 @@
+module Gitlab
+  module Lfs
+    class Router
+      def initialize(project, user, request)
+        @project = project
+        @user = user
+        @env = request.env
+        @request = request
+      end
+
+      def try_call
+        return unless @request && @request.path.present?
+
+        case @request.request_method
+        when 'GET'
+          get_response
+        when 'POST'
+          post_response
+        when 'PUT'
+          put_response
+        else
+          nil
+        end
+      end
+
+      private
+
+      def get_response
+        path_match = @request.path.match(/\/(info\/lfs|gitlab-lfs)\/objects\/([0-9a-f]{64})$/)
+        return nil unless path_match
+
+        oid = path_match[2]
+        return nil unless oid
+
+        case path_match[1]
+        when "info/lfs"
+          lfs.render_unsupported_deprecated_api
+        when "gitlab-lfs"
+          lfs.render_download_object_response(oid)
+        else
+          nil
+        end
+      end
+
+      def post_response
+        post_path = @request.path.match(/\/info\/lfs\/objects(\/batch)?$/)
+        return nil unless post_path
+
+        # Check for Batch API
+        if post_path[0].ends_with?("/info/lfs/objects/batch")
+          lfs.render_batch_operation_response
+        elsif post_path[0].ends_with?("/info/lfs/objects")
+          lfs.render_unsupported_deprecated_api
+        else
+          nil
+        end
+      end
+
+      def put_response
+        object_match = @request.path.match(/\/gitlab-lfs\/objects\/([0-9a-f]{64})\/([0-9]+)(|\/authorize){1}$/)
+        return nil if object_match.nil?
+
+        oid = object_match[1]
+        size = object_match[2].try(:to_i)
+        return nil if oid.nil? || size.nil?
+
+        # GitLab-workhorse requests
+        # 1. Try to authorize the request
+        # 2. send a request with a header containing the name of the temporary file
+        if object_match[3] && object_match[3] == '/authorize'
+          lfs.render_storage_upload_authorize_response(oid, size)
+        else
+          tmp_file_name = sanitize_tmp_filename(@request.env['HTTP_X_GITLAB_LFS_TMP'])
+          return nil unless tmp_file_name
+
+          lfs.render_storage_upload_store_response(oid, size, tmp_file_name)
+        end
+      end
+
+      def lfs
+        return unless @project
+
+        Gitlab::Lfs::Response.new(@project, @user, @request)
+      end
+
+      def sanitize_tmp_filename(name)
+        if name.present?
+          name.gsub!(/^.*(\\|\/)/, '')
+          name = name.match(/[0-9a-f]{73}/)
+          name[0] if name
+        else
+          nil
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/abstract_reference_filter.rb b/lib/gitlab/markdown/abstract_reference_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fd5b7eb9332e72715286415138f48740b727dbe3
--- /dev/null
+++ b/lib/gitlab/markdown/abstract_reference_filter.rb
@@ -0,0 +1,100 @@
+require 'gitlab/markdown'
+
+module Gitlab
+  module Markdown
+    # Issues, Snippets and Merge Requests shares similar functionality in refernce filtering.
+    # All this functionality moved to this class
+    class AbstractReferenceFilter < ReferenceFilter
+      include CrossProjectReference
+
+      def self.object_class
+        # Implement in child class
+        # Example: MergeRequest
+      end
+
+      def self.object_name
+        object_class.name.underscore
+      end
+
+      def self.object_sym
+        object_name.to_sym
+      end
+
+      def self.data_reference
+        "data-#{object_name.dasherize}"
+      end
+
+      # Public: Find references in text (like `!123` for merge requests)
+      #
+      #   AnyReferenceFilter.references_in(text) do |match, object|
+      #     "<a href=...>PREFIX#{object}</a>"
+      #   end
+      #
+      # PREFIX - symbol that detects reference (like ! for merge requests)
+      # object - reference object (snippet, merget request etc)
+      # text - String text to search.
+      #
+      # Yields the String match, the Integer referenced object ID, and an optional String
+      # of the external project reference.
+      #
+      # Returns a String replaced with the return of the block.
+      def self.references_in(text)
+        text.gsub(object_class.reference_pattern) do |match|
+          yield match, $~[object_sym].to_i, $~[:project]
+        end
+      end
+
+      def self.referenced_by(node)
+        { object_sym => LazyReference.new(object_class, node.attr(data_reference)) }
+      end
+
+      delegate :object_class, :object_sym, :references_in, to: :class
+
+      def find_object(project, id)
+        # Implement in child class
+        # Example: project.merge_requests.find
+      end
+
+      def url_for_object(object, project)
+        # Implement in child class
+        # Example: project_merge_request_url
+      end
+
+      def call
+        replace_text_nodes_matching(object_class.reference_pattern) do |content|
+          object_link_filter(content)
+        end
+      end
+
+      # Replace references (like `!123` for merge requests) in text with links
+      # to the referenced object's details page.
+      #
+      # text - String text to replace references in.
+      #
+      # Returns a String with references replaced with links. All links
+      # have `gfm` and `gfm-OBJECT_NAME` class names attached for styling.
+      def object_link_filter(text)
+        references_in(text) do |match, id, project_ref|
+          project = project_from_ref(project_ref)
+
+          if project && object = find_object(project, id)
+            title = escape_once("#{object_title}: #{object.title}")
+            klass = reference_class(object_sym)
+            data  = data_attribute(project: project.id, object_sym => object.id)
+            url = url_for_object(object, project)
+
+            %(<a href="#{url}" #{data}
+                 title="#{title}"
+                 class="#{klass}">#{match}</a>)
+          else
+            match
+          end
+        end
+      end
+
+      def object_title
+        object_class.name.titleize
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/markdown/issue_reference_filter.rb b/lib/gitlab/markdown/issue_reference_filter.rb
index 481d282f7b1c6cb30609b4d6a82f7b7379ab55fc..1ed69e2f43195a6ea4c5ad02482ec4fbba908142 100644
--- a/lib/gitlab/markdown/issue_reference_filter.rb
+++ b/lib/gitlab/markdown/issue_reference_filter.rb
@@ -6,66 +6,17 @@ module Gitlab
     # issues that do not exist are ignored.
     #
     # This filter supports cross-project references.
-    class IssueReferenceFilter < ReferenceFilter
-      include CrossProjectReference
-
-      # Public: Find `#123` issue references in text
-      #
-      #   IssueReferenceFilter.references_in(text) do |match, issue, project_ref|
-      #     "<a href=...>##{issue}</a>"
-      #   end
-      #
-      # text - String text to search.
-      #
-      # Yields the String match, the Integer issue ID, and an optional String of
-      # the external project reference.
-      #
-      # Returns a String replaced with the return of the block.
-      def self.references_in(text)
-        text.gsub(Issue.reference_pattern) do |match|
-          yield match, $~[:issue].to_i, $~[:project]
-        end
-      end
-
-      def self.referenced_by(node)
-        { issue: LazyReference.new(Issue, node.attr("data-issue")) }
-      end
-
-      def call
-        replace_text_nodes_matching(Issue.reference_pattern) do |content|
-          issue_link_filter(content)
-        end
+    class IssueReferenceFilter < AbstractReferenceFilter
+      def self.object_class
+        Issue
       end
 
-      # Replace `#123` issue references in text with links to the referenced
-      # issue's details page.
-      #
-      # text - String text to replace references in.
-      #
-      # Returns a String with `#123` references replaced with links. All links
-      # have `gfm` and `gfm-issue` class names attached for styling.
-      def issue_link_filter(text)
-        self.class.references_in(text) do |match, id, project_ref|
-          project = self.project_from_ref(project_ref)
-
-          if project && issue = project.get_issue(id)
-            url = url_for_issue(id, project, only_path: context[:only_path])
-
-            title = escape_once("Issue: #{issue.title}")
-            klass = reference_class(:issue)
-            data  = data_attribute(project: project.id, issue: issue.id)
-
-            %(<a href="#{url}" #{data}
-                 title="#{title}"
-                 class="#{klass}">#{match}</a>)
-          else
-            match
-          end
-        end
+      def find_object(project, id)
+        project.get_issue(id)
       end
 
-      def url_for_issue(*args)
-        IssuesHelper.url_for_issue(*args)
+      def url_for_object(issue, project)
+        IssuesHelper.url_for_issue(issue.iid, project, only_path: context[:only_path])
       end
     end
   end
diff --git a/lib/gitlab/markdown/label_reference_filter.rb b/lib/gitlab/markdown/label_reference_filter.rb
index 618acb7a57814c32c1bc31f131a2d6cde8fdb40d..13581b8fb136671a0c51b8dd74e8e1e2a0b22f65 100644
--- a/lib/gitlab/markdown/label_reference_filter.rb
+++ b/lib/gitlab/markdown/label_reference_filter.rb
@@ -60,8 +60,7 @@ module Gitlab
       def url_for_label(project, label)
         h = Gitlab::Application.routes.url_helpers
         h.namespace_project_issues_path(project.namespace, project,
-                                        label_name: label.name,
-                                        only_path: context[:only_path])
+                                        label_name: label.name)
       end
 
       def render_colored_label(label)
diff --git a/lib/gitlab/markdown/merge_request_reference_filter.rb b/lib/gitlab/markdown/merge_request_reference_filter.rb
index 5bc632698082556ec3132314a941fc14e12a924a..1f47f03c94ebf5f75041c23b2b80cee7fcc0cb4d 100644
--- a/lib/gitlab/markdown/merge_request_reference_filter.rb
+++ b/lib/gitlab/markdown/merge_request_reference_filter.rb
@@ -6,65 +6,16 @@ module Gitlab
     # to merge requests that do not exist are ignored.
     #
     # This filter supports cross-project references.
-    class MergeRequestReferenceFilter < ReferenceFilter
-      include CrossProjectReference
-
-      # Public: Find `!123` merge request references in text
-      #
-      #   MergeRequestReferenceFilter.references_in(text) do |match, merge_request, project_ref|
-      #     "<a href=...>##{merge_request}</a>"
-      #   end
-      #
-      # text - String text to search.
-      #
-      # Yields the String match, the Integer merge request ID, and an optional
-      # String of the external project reference.
-      #
-      # Returns a String replaced with the return of the block.
-      def self.references_in(text)
-        text.gsub(MergeRequest.reference_pattern) do |match|
-          yield match, $~[:merge_request].to_i, $~[:project]
-        end
-      end
-
-      def self.referenced_by(node)
-        { merge_request: LazyReference.new(MergeRequest, node.attr("data-merge-request")) }
-      end
-
-      def call
-        replace_text_nodes_matching(MergeRequest.reference_pattern) do |content|
-          merge_request_link_filter(content)
-        end
+    class MergeRequestReferenceFilter < AbstractReferenceFilter
+      def self.object_class
+        MergeRequest
       end
 
-      # Replace `!123` merge request references in text with links to the
-      # referenced merge request's details page.
-      #
-      # text - String text to replace references in.
-      #
-      # Returns a String with `!123` references replaced with links. All links
-      # have `gfm` and `gfm-merge_request` class names attached for styling.
-      def merge_request_link_filter(text)
-        self.class.references_in(text) do |match, id, project_ref|
-          project = self.project_from_ref(project_ref)
-
-          if project && merge_request = project.merge_requests.find_by(iid: id)
-            title = escape_once("Merge Request: #{merge_request.title}")
-            klass = reference_class(:merge_request)
-            data  = data_attribute(project: project.id, merge_request: merge_request.id)
-
-            url = url_for_merge_request(merge_request, project)
-
-            %(<a href="#{url}" #{data}
-                 title="#{title}"
-                 class="#{klass}">#{match}</a>)
-          else
-            match
-          end
-        end
+      def find_object(project, id)
+        project.merge_requests.find_by(iid: id)
       end
 
-      def url_for_merge_request(mr, project)
+      def url_for_object(mr, project)
         h = Gitlab::Application.routes.url_helpers
         h.namespace_project_merge_request_url(project.namespace, project, mr,
                                             only_path: context[:only_path])
diff --git a/lib/gitlab/markdown/relative_link_filter.rb b/lib/gitlab/markdown/relative_link_filter.rb
index 6ee3d1ce03903465b9bc694ab26cedb5271c630a..632be4d754255bbf4803dbe3c6f2e12ace6a4b9a 100644
--- a/lib/gitlab/markdown/relative_link_filter.rb
+++ b/lib/gitlab/markdown/relative_link_filter.rb
@@ -51,7 +51,7 @@ module Gitlab
           relative_url_root,
           context[:project].path_with_namespace,
           path_type(file_path),
-          ref || 'master',  # assume that if no ref exists we can point to master
+          ref || context[:project].default_branch,  # if no ref exists, point to the default branch
           file_path
         ].compact.join('/').squeeze('/').chomp('/')
 
diff --git a/lib/gitlab/markdown/snippet_reference_filter.rb b/lib/gitlab/markdown/snippet_reference_filter.rb
index f783f951711d974f656f2c3bebad63bb131034d1..f7bd07c2a34f210d0c71bcab78daa02cce11e693 100644
--- a/lib/gitlab/markdown/snippet_reference_filter.rb
+++ b/lib/gitlab/markdown/snippet_reference_filter.rb
@@ -6,65 +6,16 @@ module Gitlab
     # snippets that do not exist are ignored.
     #
     # This filter supports cross-project references.
-    class SnippetReferenceFilter < ReferenceFilter
-      include CrossProjectReference
-
-      # Public: Find `$123` snippet references in text
-      #
-      #   SnippetReferenceFilter.references_in(text) do |match, snippet|
-      #     "<a href=...>$#{snippet}</a>"
-      #   end
-      #
-      # text - String text to search.
-      #
-      # Yields the String match, the Integer snippet ID, and an optional String
-      # of the external project reference.
-      #
-      # Returns a String replaced with the return of the block.
-      def self.references_in(text)
-        text.gsub(Snippet.reference_pattern) do |match|
-          yield match, $~[:snippet].to_i, $~[:project]
-        end
-      end
-
-      def self.referenced_by(node)
-        { snippet: LazyReference.new(Snippet, node.attr("data-snippet")) }
-      end
-
-      def call
-        replace_text_nodes_matching(Snippet.reference_pattern) do |content|
-          snippet_link_filter(content)
-        end
+    class SnippetReferenceFilter < AbstractReferenceFilter
+      def self.object_class
+        Snippet
       end
 
-      # Replace `$123` snippet references in text with links to the referenced
-      # snippets's details page.
-      #
-      # text - String text to replace references in.
-      #
-      # Returns a String with `$123` references replaced with links. All links
-      # have `gfm` and `gfm-snippet` class names attached for styling.
-      def snippet_link_filter(text)
-        self.class.references_in(text) do |match, id, project_ref|
-          project = self.project_from_ref(project_ref)
-
-          if project && snippet = project.snippets.find_by(id: id)
-            title = escape_once("Snippet: #{snippet.title}")
-            klass = reference_class(:snippet)
-            data  = data_attribute(project: project.id, snippet: snippet.id)
-
-            url = url_for_snippet(snippet, project)
-
-            %(<a href="#{url}" #{data}
-                 title="#{title}"
-                 class="#{klass}">#{match}</a>)
-          else
-            match
-          end
-        end
+      def find_object(project, id)
+        project.snippets.find_by(id: id)
       end
 
-      def url_for_snippet(snippet, project)
+      def url_for_object(snippet, project)
         h = Gitlab::Application.routes.url_helpers
         h.namespace_project_snippet_url(project.namespace, project, snippet,
                                         only_path: context[:only_path])
diff --git a/lib/gitlab/markdown/user_reference_filter.rb b/lib/gitlab/markdown/user_reference_filter.rb
index 2a594e1662ed9f2e9d00b01542ecafeb58e49ee4..ab5e1f6fe9eda11e4794b8fb6db570546b1eca26 100644
--- a/lib/gitlab/markdown/user_reference_filter.rb
+++ b/lib/gitlab/markdown/user_reference_filter.rb
@@ -85,13 +85,12 @@ module Gitlab
 
       def link_to_all
         project = context[:project]
-
         url = urls.namespace_project_url(project.namespace, project,
                                          only_path: context[:only_path])
         data = data_attribute(project: project.id)
-
         text = User.reference_prefix + 'all'
-        %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
+
+        link_tag(url, data, text)
       end
 
       def link_to_namespace(namespace)
@@ -105,16 +104,20 @@ module Gitlab
       def link_to_group(group, namespace)
         url = urls.group_url(group, only_path: context[:only_path])
         data = data_attribute(group: namespace.id)
-
         text = Group.reference_prefix + group
-        %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
+
+        link_tag(url, data, text)
       end
 
       def link_to_user(user, namespace)
         url = urls.user_url(user, only_path: context[:only_path])
         data = data_attribute(user: namespace.owner_id)
-
         text = User.reference_prefix + user
+
+        link_tag(url, data, text)
+      end
+
+      def link_tag(url, data, text)
         %(<a href="#{url}" #{data} class="#{link_class}">#{text}</a>)
       end
     end
diff --git a/lib/gitlab/seeder.rb b/lib/gitlab/seeder.rb
index 31aa3528c4c37a4aa4416da61e6d0df01368b9c0..2ef0e982256cdefa8f60090221d4534053e2ed6e 100644
--- a/lib/gitlab/seeder.rb
+++ b/lib/gitlab/seeder.rb
@@ -14,7 +14,7 @@ module Gitlab
 
     def self.mute_mailer
       code = <<-eos
-def Notify.delay
+def Notify.deliver_later
   self
 end
       eos
diff --git a/lib/gitlab/sherlock/transaction.rb b/lib/gitlab/sherlock/transaction.rb
index d87a4c9bb4a82a5e59dc6dbe87dfe0c6436e6673..3489fb251b6be312cd6d63ed5103e289776cad3e 100644
--- a/lib/gitlab/sherlock/transaction.rb
+++ b/lib/gitlab/sherlock/transaction.rb
@@ -36,6 +36,11 @@ module Gitlab
         @duration ||= started_at && finished_at ? finished_at - started_at : 0
       end
 
+      # Returns the total query duration in seconds.
+      def query_duration
+        @query_duration ||= @queries.map { |q| q.duration }.inject(:+) / 1000.0
+      end
+
       def to_param
         @id
       end
diff --git a/lib/gitlab/sql/union.rb b/lib/gitlab/sql/union.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1cd89b3a9c40b5de369023805e0942c014be5e3d
--- /dev/null
+++ b/lib/gitlab/sql/union.rb
@@ -0,0 +1,34 @@
+module Gitlab
+  module SQL
+    # Class for building SQL UNION statements.
+    #
+    # ORDER BYs are dropped from the relations as the final sort order is not
+    # guaranteed any way.
+    #
+    # Example usage:
+    #
+    #     union = Gitlab::SQL::Union.new(user.personal_projects, user.projects)
+    #     sql   = union.to_sql
+    #
+    #     Project.where("id IN (#{sql})")
+    class Union
+      def initialize(relations)
+        @relations = relations
+      end
+
+      def to_sql
+        # Some relations may include placeholders for prepared statements, these
+        # aren't incremented properly when joining relations together this way.
+        # By using "unprepared_statements" we remove the usage of placeholders
+        # (thus fixing this problem), at a slight performance cost.
+        fragments = ActiveRecord::Base.connection.unprepared_statement do
+          @relations.map do |rel|
+            rel.reorder(nil).to_sql
+          end
+        end
+
+        fragments.join("\nUNION\n")
+      end
+    end
+  end
+end
diff --git a/lib/support/nginx/gitlab b/lib/support/nginx/gitlab
index 0a7a4118077bb271ae3bf700c017c0e390380dbf..2a79fbdcf93c8fddfd1c487318f88c09d0bbaf2e 100644
--- a/lib/support/nginx/gitlab
+++ b/lib/support/nginx/gitlab
@@ -44,7 +44,7 @@ upstream gitlab-workhorse {
 
 ## Normal HTTP host
 server {
-  ## Either remove "default_server" from the listen line below, 
+  ## Either remove "default_server" from the listen line below,
   ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
   ## to be served if you visit any address that your server responds to, eg.
   ## the ip address of the server (http://x.x.x.x/)n 0.0.0.0:80 default_server;
@@ -67,7 +67,7 @@ server {
   location / {
     ## Serve static files from defined root folder.
     ## @gitlab is a named location for the upstream fallback, see below.
-    try_files $uri $uri/index.html $uri.html @gitlab;
+    try_files $uri /index.html $uri.html @gitlab;
   }
 
   ## We route uploads through GitLab to prevent XSS and enforce access control.
@@ -113,19 +113,29 @@ server {
     proxy_pass http://gitlab;
   }
 
+  location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
   location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
   location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
   location ~ ^/api/v3/projects/.*/repository/archive {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
@@ -133,21 +143,22 @@ server {
 
   # Build artifacts should be submitted to this location
   location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
-      client_max_body_size 0;
-      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
-      error_page 418 = @gitlab-workhorse;
-      return 418;
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
   }
 
   # Build artifacts should be submitted to this location
   location ~ /ci/api/v1/builds/[0-9]+/artifacts {
-      client_max_body_size 0;
-      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
-      error_page 418 = @gitlab-workhorse;
-      return 418;
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
   }
 
   location @gitlab-workhorse {
+    client_max_body_size 0;
     ## If you use HTTPS make sure you disable gzip compression
     ## to be safe against BREACH attack.
     # gzip off;
diff --git a/lib/support/nginx/gitlab-ssl b/lib/support/nginx/gitlab-ssl
index b463d5b6aa991b2a45766b25be853f6d65749e9e..016f7a536fb854e38adc1fbc9083e8e7f93f0c9d 100644
--- a/lib/support/nginx/gitlab-ssl
+++ b/lib/support/nginx/gitlab-ssl
@@ -48,7 +48,7 @@ upstream gitlab-workhorse {
 
 ## Redirects all HTTP traffic to the HTTPS host
 server {
-  ## Either remove "default_server" from the listen line below, 
+  ## Either remove "default_server" from the listen line below,
   ## or delete the /etc/nginx/sites-enabled/default file. This will cause gitlab
   ## to be served if you visit any address that your server responds to, eg.
   ## the ip address of the server (http://x.x.x.x/)
@@ -112,7 +112,7 @@ server {
   location / {
     ## Serve static files from defined root folder.
     ## @gitlab is a named location for the upstream fallback, see below.
-    try_files $uri $uri/index.html $uri.html @gitlab;
+    try_files $uri /index.html $uri.html @gitlab;
   }
 
   ## We route uploads through GitLab to prevent XSS and enforce access control.
@@ -160,19 +160,29 @@ server {
     proxy_pass http://gitlab;
   }
 
+  location ~ ^/[\w\.-]+/[\w\.-]+/gitlab-lfs/objects {
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
+  }
+
   location ~ ^/[\w\.-]+/[\w\.-]+/(info/refs|git-upload-pack|git-receive-pack)$ {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
   location ~ ^/[\w\.-]+/[\w\.-]+/repository/archive {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
   }
 
   location ~ ^/api/v3/projects/.*/repository/archive {
+    client_max_body_size 0;
     # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
     error_page 418 = @gitlab-workhorse;
     return 418;
@@ -180,21 +190,22 @@ server {
 
   # Build artifacts should be submitted to this location
   location ~ ^/[\w\.-]+/[\w\.-]+/builds/download {
-      client_max_body_size 0;
-      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
-      error_page 418 = @gitlab-workhorse;
-      return 418;
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
   }
 
   # Build artifacts should be submitted to this location
   location ~ /ci/api/v1/builds/[0-9]+/artifacts {
-      client_max_body_size 0;
-      # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
-      error_page 418 = @gitlab-workhorse;
-      return 418;
+    client_max_body_size 0;
+    # 'Error' 418 is a hack to re-use the @gitlab-workhorse block
+    error_page 418 = @gitlab-workhorse;
+    return 418;
   }
 
   location @gitlab-workhorse {
+    client_max_body_size 0;
     ## If you use HTTPS make sure you disable gzip compression
     ## to be safe against BREACH attack.
     gzip off;
diff --git a/lib/tasks/flay.rake b/lib/tasks/flay.rake
index 5efffc2cdaccf13598c347a955e7ac4c84f1c371..e9587595fef45e9f4840cdc251381557af2801d6 100644
--- a/lib/tasks/flay.rake
+++ b/lib/tasks/flay.rake
@@ -1,6 +1,6 @@
 desc 'Code duplication analyze via flay'
 task :flay do
-  output = %x(bundle exec flay app/ lib/gitlab/)
+  output = %x(bundle exec flay --mass 35 app/ lib/gitlab/)
 
   if output.include? "Similar code found"
     puts output
diff --git a/lib/tasks/gitlab/backup.rake b/lib/tasks/gitlab/backup.rake
index 3c46bcea40edd172754113d792537820d8f2ce06..cb4abe13799f426cac93a62a95a3d770626ec665 100644
--- a/lib/tasks/gitlab/backup.rake
+++ b/lib/tasks/gitlab/backup.rake
@@ -13,6 +13,7 @@ namespace :gitlab do
       Rake::Task["gitlab:backup:uploads:create"].invoke
       Rake::Task["gitlab:backup:builds:create"].invoke
       Rake::Task["gitlab:backup:artifacts:create"].invoke
+      Rake::Task["gitlab:backup:lfs:create"].invoke
 
       backup = Backup::Manager.new
       backup.pack
@@ -34,6 +35,7 @@ namespace :gitlab do
       Rake::Task["gitlab:backup:uploads:restore"].invoke unless backup.skipped?("uploads")
       Rake::Task["gitlab:backup:builds:restore"].invoke unless backup.skipped?("builds")
       Rake::Task["gitlab:backup:artifacts:restore"].invoke unless backup.skipped?("artifacts")
+      Rake::Task["gitlab:backup:lfs:restore"].invoke unless backup.skipped?("lfs")
       Rake::Task["gitlab:shell:setup"].invoke
 
       backup.cleanup
@@ -134,6 +136,25 @@ namespace :gitlab do
       end
     end
 
+    namespace :lfs do
+      task create: :environment do
+        $progress.puts "Dumping lfs objects ... ".blue
+
+        if ENV["SKIP"] && ENV["SKIP"].include?("lfs")
+          $progress.puts "[SKIPPED]".cyan
+        else
+          Backup::Lfs.new.dump
+          $progress.puts "done".green
+        end
+      end
+
+      task restore: :environment do
+        $progress.puts "Restoring lfs objects ... ".blue
+        Backup::Lfs.new.restore
+        $progress.puts "done".green
+      end
+    end
+
     def configure_cron_mode
       if ENV['CRON']
         # We need an object we can say 'puts' and 'print' to; let's use a
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index c95b6540ebc6e2ce4315f2a74b2d3d2cf050b383..efb863a8764b40b5285832e418b79333c8540a46 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -2,16 +2,6 @@ module Gitlab
   class TaskAbortedByUserError < StandardError; end
 end
 
-unless STDOUT.isatty
-  module Colored
-    extend self
-
-    def colorize(string, options={})
-      string
-    end
-  end
-end
-
 namespace :gitlab do
 
   # Ask if the user wants to continue
@@ -103,7 +93,7 @@ namespace :gitlab do
       gitlab_user = Gitlab.config.gitlab.user
       current_user = run(%W(whoami)).chomp
       unless current_user == gitlab_user
-        puts "#{Colored.color(:black)+Colored.color(:on_yellow)} Warning #{Colored.extra(:clear)}"
+        puts " Warning ".colorize(:black).on_yellow
         puts "  You are running as user #{current_user.magenta}, we hope you know what you are doing."
         puts "  Things may work\/fail for the wrong reasons."
         puts "  For correct results you should run this as user #{gitlab_user.magenta}."
diff --git a/lib/tasks/grape.rake b/lib/tasks/grape.rake
new file mode 100644
index 0000000000000000000000000000000000000000..9980e0b7984b6aee4a9e63544810839bd2b66498
--- /dev/null
+++ b/lib/tasks/grape.rake
@@ -0,0 +1,8 @@
+namespace :grape do
+  desc 'Print compiled grape routes'
+  task routes: :environment do
+    API::API.routes.each do |route|
+      puts route
+    end
+  end
+end
diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh
index dca5e1c5db3b141d7e0f34c4dd0fbc48f2f7e452..119cc90fc1ebde8edb6aa5d5289aae77ac35cffb 100755
--- a/scripts/prepare_build.sh
+++ b/scripts/prepare_build.sh
@@ -1,7 +1,7 @@
 #!/bin/bash
 if [ -f /.dockerinit ]; then
-    wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_1.9.0-1+b1_amd64.deb
-    dpkg -i phantomjs_1.9.0-1+b1_amd64.deb
+    wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb
+    dpkg -i phantomjs_1.9.8-0jessie_amd64.deb
 
     apt-get update -qq
     apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client
diff --git a/spec/benchmarks/finders/issues_finder_spec.rb b/spec/benchmarks/finders/issues_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b57a33004a48ca6afa01dfa114eaac3e2dc46eca
--- /dev/null
+++ b/spec/benchmarks/finders/issues_finder_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe IssuesFinder, benchmark: true do
+  describe '#execute' do
+    let(:user) { create(:user) }
+    let(:project) { create(:project, :public) }
+
+    let(:label1) { create(:label, project: project, title: 'A') }
+    let(:label2) { create(:label, project: project, title: 'B') }
+
+    before do
+      10.times do |n|
+        issue = create(:issue, author: user, project: project)
+
+        if n > 4
+          create(:label_link, label: label1, target: issue)
+          create(:label_link, label: label2, target: issue)
+        end
+      end
+    end
+
+    describe 'retrieving issues without labels' do
+      let(:finder) do
+        IssuesFinder.new(user, scope: 'all', label_name: Label::None.title,
+                               state: 'opened')
+      end
+
+      benchmark_subject { finder.execute }
+
+      it { is_expected.to iterate_per_second(2000) }
+    end
+
+    describe 'retrieving issues with labels' do
+      let(:finder) do
+        IssuesFinder.new(user, scope: 'all', label_name: label1.title,
+                               state: 'opened')
+      end
+
+      benchmark_subject { finder.execute }
+
+      it { is_expected.to iterate_per_second(1000) }
+    end
+
+    describe 'retrieving issues for a single project' do
+      let(:finder) do
+        IssuesFinder.new(user, scope: 'all', label_name: Label::None.title,
+                               state: 'opened', project_id: project.id)
+      end
+
+      benchmark_subject { finder.execute }
+
+      it { is_expected.to iterate_per_second(2000) }
+    end
+  end
+end
diff --git a/spec/controllers/abuse_reports_controller_spec.rb b/spec/controllers/abuse_reports_controller_spec.rb
index 0faab8d7ff0b2e3885325bf5a9f48947bcc55111..15824a1c67f2149d5e02c8d75c6fe00a92c9cbc2 100644
--- a/spec/controllers/abuse_reports_controller_spec.rb
+++ b/spec/controllers/abuse_reports_controller_spec.rb
@@ -18,27 +18,31 @@ describe AbuseReportsController do
       end
 
       it "sends a notification email" do
-        post :create,
-          abuse_report: {
-            user_id: user.id,
-            message: message
-          }
-
-        email = ActionMailer::Base.deliveries.last
-
-        expect(email.to).to eq([admin_email])
-        expect(email.subject).to include(user.username)
-        expect(email.text_part.body).to include(message)
-      end
-
-      it "saves the abuse report" do
-        expect do
+        perform_enqueued_jobs do
           post :create,
             abuse_report: {
               user_id: user.id,
               message: message
             }
-        end.to change { AbuseReport.count }.by(1)
+
+          email = ActionMailer::Base.deliveries.last
+
+          expect(email.to).to eq([admin_email])
+          expect(email.subject).to include(user.username)
+          expect(email.text_part.body).to include(message)
+        end
+      end
+
+      it "saves the abuse report" do
+        perform_enqueued_jobs do
+          expect do
+            post :create,
+              abuse_report: {
+                user_id: user.id,
+                message: message
+              }
+          end.to change { AbuseReport.count }.by(1)
+        end
       end
     end
 
diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d7a7ba1c5b6601194dac6a17ca0e7e58e3c40e00
--- /dev/null
+++ b/spec/controllers/admin/impersonation_controller_spec.rb
@@ -0,0 +1,19 @@
+require 'spec_helper'
+
+describe Admin::ImpersonationController do
+  let(:admin) { create(:admin) }
+
+  before do
+    sign_in(admin)
+  end
+
+  describe 'CREATE #impersonation when blocked' do
+    let(:blocked_user) { create(:user, state: :blocked) }
+
+    it 'does not allow impersonation' do
+      post :create, id: blocked_user.username
+
+      expect(flash[:alert]).to eq 'You cannot impersonate a blocked user'
+    end
+  end
+end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index fcbe62cace85c9f243e489679fb82b486d8fc488..8b7af4d3a0aa94542ab494ca663693142ac4b409 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -7,21 +7,6 @@ describe Admin::UsersController do
     sign_in(admin)
   end
 
-  describe 'POST login_as' do
-    let(:user) { create(:user) }
-
-    it 'logs admin as another user' do
-      expect(warden.authenticate(scope: :user)).not_to eq(user)
-      post :login_as, id: user.username
-      expect(warden.authenticate(scope: :user)).to eq(user)
-    end
-
-    it 'redirects user to homepage' do
-      post :login_as, id: user.username
-      expect(response).to redirect_to(root_path)
-    end
-  end
-
   describe 'DELETE #user with projects' do
     let(:user) { create(:user) }
     let(:project) { create(:project, namespace: user.namespace) }
diff --git a/spec/controllers/autocomplete_controller_spec.rb b/spec/controllers/autocomplete_controller_spec.rb
index aa8d6cb807f968c4b8af91a540fc7d010d0d3996..85379a8e9845da4b527cee5495f9670121f786df 100644
--- a/spec/controllers/autocomplete_controller_spec.rb
+++ b/spec/controllers/autocomplete_controller_spec.rb
@@ -114,7 +114,7 @@ describe AutocompleteController do
         get(:users, project_id: project.id)
       end
 
-      it { expect(response.status).to eq(302) }
+      it { expect(response.status).to eq(404) }
     end
 
     describe 'GET #users with unknown project' do
@@ -122,7 +122,7 @@ describe AutocompleteController do
         get(:users, project_id: 'unknown')
       end
 
-      it { expect(response.status).to eq(302) }
+      it { expect(response.status).to eq(404) }
     end
 
     describe 'GET #users with inaccessible group' do
@@ -131,7 +131,7 @@ describe AutocompleteController do
         get(:users, group_id: user.namespace.id)
       end
 
-      it { expect(response.status).to eq(302) }
+      it { expect(response.status).to eq(404) }
     end
 
     describe 'GET #users with no project' do
@@ -139,7 +139,8 @@ describe AutocompleteController do
         get(:users)
       end
 
-      it { expect(response.status).to eq(302) }
+      it { expect(body).to be_kind_of(Array) }
+      it { expect(body.size).to eq 0 }
     end
   end
 end
diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb
index bb3d87f384081f488d4fd500e837be9a72d75661..5337a69e84b34c8a0a0848347a16d8569919ea36 100644
--- a/spec/controllers/commit_controller_spec.rb
+++ b/spec/controllers/commit_controller_spec.rb
@@ -69,6 +69,21 @@ describe Projects::CommitController do
 
         expect(response.body).to start_with("diff --git")
       end
+      
+      it "should really only be a git diff without whitespace changes" do
+        get(:show,
+            namespace_id: project.namespace.to_param,
+            project_id: project.to_param,
+            id: '66eceea0db202bb39c4e445e8ca28689645366c5',
+            # id: commit.id,
+            format: format,
+            w: 1)
+
+        expect(response.body).to start_with("diff --git")
+        # without whitespace option, there are more than 2 diff_splits
+        diff_splits = assigns(:diffs)[0].diff.split("\n")
+        expect(diff_splits.length).to be <= 2
+      end
     end
 
     describe "as patch" do
diff --git a/spec/controllers/projects/compare_controller_spec.rb b/spec/controllers/projects/compare_controller_spec.rb
index 2a447248b704d42a706c5b8940597988d4d87cba..be19f1abc534bf7c38435e748d5113f07685e680 100644
--- a/spec/controllers/projects/compare_controller_spec.rb
+++ b/spec/controllers/projects/compare_controller_spec.rb
@@ -23,6 +23,22 @@ describe Projects::CompareController do
     expect(assigns(:commits).length).to be >= 1
   end
 
+  it 'compare should show some diffs with ignore whitespace change option' do
+    get(:show,
+        namespace_id: project.namespace.to_param,
+        project_id: project.to_param,
+        from: '08f22f25',
+        to: '66eceea0',
+        w: 1)
+
+    expect(response).to be_success
+    expect(assigns(:diffs).length).to be >= 1
+    expect(assigns(:commits).length).to be >= 1
+    # without whitespace option, there are more than 2 diff_splits
+    diff_splits = assigns(:diffs)[0].diff.split("\n")
+    expect(diff_splits.length).to be <= 2
+  end
+
   describe 'non-existent refs' do
     it 'invalid source ref' do
       get(:show,
diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb
index b8db8591709b5932abadd5ed98f7b6ba909f1348..3e5e1fa87ae710b80cbddc2c40d63e7e8ecb56af 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -147,6 +147,34 @@ describe Projects::MergeRequestsController do
     end
   end
 
+  describe 'GET diffs with ignore_whitespace_change' do
+    def go(format: 'html')
+      get :diffs,
+          namespace_id: project.namespace.to_param,
+          project_id: project.to_param,
+          id: merge_request.iid,
+          format: format,
+          w: 1
+    end
+
+    context 'as html' do
+      it 'renders the diff template' do
+        go
+
+        expect(response).to render_template('diffs')
+      end
+    end
+    
+    context 'as json' do
+      it 'renders the diffs template to a string' do
+        go format: 'json'
+
+        expect(response).to render_template('projects/merge_requests/show/_diffs')
+        expect(JSON.parse(response.body)).to have_key('html')
+      end
+    end
+  end
+
   describe 'GET commits' do
     def go(format: 'html')
       get :commits,
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 4bb47c6b02559c73c815f659801c16060aa13b80..665526fde9312c5f16ef0aab56f1dedeeb1552e5 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -88,6 +88,22 @@ describe ProjectsController do
     end
   end
 
+  describe "#destroy" do
+    let(:admin) { create(:admin) }
+
+    it "redirects to the dashboard" do
+      controller.instance_variable_set(:@project, project)
+      sign_in(admin)
+
+      orig_id = project.id
+      delete :destroy, namespace_id: project.namespace.path, id: project.path
+
+      expect { Project.find(orig_id) }.to raise_error(ActiveRecord::RecordNotFound)
+      expect(response.status).to eq(302)
+      expect(response).to redirect_to(dashboard_projects_path)
+    end
+  end
+
   describe "POST #toggle_star" do
     it "toggles star if user is signed in" do
       sign_in(user)
diff --git a/spec/controllers/snippets_controller_spec.rb b/spec/controllers/snippets_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b3dcb52c5003bcd79bc187805f0766c7e7ca4992
--- /dev/null
+++ b/spec/controllers/snippets_controller_spec.rb
@@ -0,0 +1,233 @@
+require 'spec_helper'
+
+describe SnippetsController do
+  describe 'GET #show' do
+    let(:user) { create(:user) }
+
+    context 'when the personal snippet is private' do
+      let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        context 'when signed in user is not the author' do
+          let(:other_author) { create(:author) }
+          let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
+
+          it 'responds with status 404' do
+            get :show, id: other_personal_snippet.to_param
+
+            expect(response.status).to eq(404)
+          end
+        end
+
+        context 'when signed in user is the author' do
+          it 'renders the snippet' do
+            get :show, id: personal_snippet.to_param
+
+            expect(assigns(:snippet)).to eq(personal_snippet)
+            expect(response.status).to eq(200)
+          end
+        end
+      end
+
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :show, id: personal_snippet.to_param
+
+          expect(response).to redirect_to(new_user_session_path)
+        end
+      end
+    end
+
+    context 'when the personal snippet is internal' do
+      let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'renders the snippet' do
+          get :show, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :show, id: personal_snippet.to_param
+
+          expect(response).to redirect_to(new_user_session_path)
+        end
+      end
+    end
+
+    context 'when the personal snippet is public' do
+      let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'renders the snippet' do
+          get :show, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'renders the snippet' do
+          get :show, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+    end
+
+    context 'when the personal snippet does not exist' do
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'responds with status 404' do
+          get :show, id: 'doesntexist'
+
+          expect(response.status).to eq(404)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'responds with status 404' do
+          get :show, id: 'doesntexist'
+
+          expect(response.status).to eq(404)
+        end
+      end
+    end
+  end
+
+  describe 'GET #raw' do
+    let(:user) { create(:user) }
+
+    context 'when the personal snippet is private' do
+      let(:personal_snippet) { create(:personal_snippet, :private, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        context 'when signed in user is not the author' do
+          let(:other_author) { create(:author) }
+          let(:other_personal_snippet) { create(:personal_snippet, :private, author: other_author) }
+
+          it 'responds with status 404' do
+            get :raw, id: other_personal_snippet.to_param
+
+            expect(response.status).to eq(404)
+          end
+        end
+
+        context 'when signed in user is the author' do
+          it 'renders the raw snippet' do
+            get :raw, id: personal_snippet.to_param
+
+            expect(assigns(:snippet)).to eq(personal_snippet)
+            expect(response.status).to eq(200)
+          end
+        end
+      end
+
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :raw, id: personal_snippet.to_param
+
+          expect(response).to redirect_to(new_user_session_path)
+        end
+      end
+    end
+
+    context 'when the personal snippet is internal' do
+      let(:personal_snippet) { create(:personal_snippet, :internal, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'renders the raw snippet' do
+          get :raw, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'redirects to the sign in page' do
+          get :raw, id: personal_snippet.to_param
+
+          expect(response).to redirect_to(new_user_session_path)
+        end
+      end
+    end
+
+    context 'when the personal snippet is public' do
+      let(:personal_snippet) { create(:personal_snippet, :public, author: user) }
+
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'renders the raw snippet' do
+          get :raw, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'renders the raw snippet' do
+          get :raw, id: personal_snippet.to_param
+
+          expect(assigns(:snippet)).to eq(personal_snippet)
+          expect(response.status).to eq(200)
+        end
+      end
+    end
+
+    context 'when the personal snippet does not exist' do
+      context 'when signed in' do
+        before do
+          sign_in(user)
+        end
+
+        it 'responds with status 404' do
+          get :raw, id: 'doesntexist'
+
+          expect(response.status).to eq(404)
+        end
+      end
+
+      context 'when not signed in' do
+        it 'responds with status 404' do
+          get :raw, id: 'doesntexist'
+
+          expect(response.status).to eq(404)
+        end
+      end
+    end
+  end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 9f89101d7f720a56b1ab33fb36f18299c296617b..104a5f501432f7c25b520e16b91ef26ec3d4be12 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -16,13 +16,26 @@ describe UsersController do
     context 'with rendered views' do
       render_views
 
-      it 'renders the show template' do
-        sign_in(user)
+      describe 'when logged in' do
+        before do
+          sign_in(user)
+        end
 
-        get :show, username: user.username
+        it 'renders the show template' do
+          get :show, username: user.username
 
-        expect(response).to be_success
-        expect(response).to render_template('show')
+          expect(response).to be_success
+          expect(response).to render_template('show')
+        end
+      end
+
+      describe 'when logged out' do
+        it 'renders the show template' do
+          get :show, username: user.username
+
+          expect(response).to be_success
+          expect(response).to render_template('show')
+        end
       end
     end
   end
diff --git a/spec/factories.rb b/spec/factories.rb
index 200f18f660d52b4b32e36c7fadff1f3702c66051..4bf93adabe27c31e5977daa3eb85726ce88e2a17 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -165,6 +165,18 @@ FactoryGirl.define do
     title
     content
     file_name
+
+    trait :public do
+      visibility_level Gitlab::VisibilityLevel::PUBLIC
+    end
+
+    trait :internal do
+      visibility_level Gitlab::VisibilityLevel::INTERNAL
+    end
+
+    trait :private do
+      visibility_level Gitlab::VisibilityLevel::PRIVATE
+    end
   end
 
   factory :snippet do
diff --git a/spec/factories/ci/projects.rb b/spec/factories/ci/projects.rb
index 1183a190353e7f97e827c0709278712e5e625e89..11cb8c9eeaa25647707ff80364b7b8f9a4c7730d 100644
--- a/spec/factories/ci/projects.rb
+++ b/spec/factories/ci/projects.rb
@@ -31,16 +31,20 @@ FactoryGirl.define do
   factory :ci_project_without_token, class: Ci::Project do
     default_ref 'master'
 
-    gl_project factory: :empty_project
-
     shared_runners_enabled false
 
     factory :ci_project do
       token 'iPWx6WM4lhHNedGfBpPJNP'
     end
 
-    factory :ci_public_project do
-      public true
+    initialize_with do
+      # TODO:
+      # this is required, because builds_enabled is initialized when Project is created
+      # and this create gitlab_ci_project if builds is set to true
+      # here we take created gitlab_ci_project and update it's attributes
+      ci_project = create(:empty_project).ensure_gitlab_ci_project
+      ci_project.update_attributes(attributes)
+      ci_project
     end
   end
 end
diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb
index 6829387c660c86490e08d806fe90f315a8ee29f1..8b12ee11af5642eeb41153a733efc1188a588169 100644
--- a/spec/factories/labels.rb
+++ b/spec/factories/labels.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 # Read about factories at https://github.com/thoughtbot/factory_girl
diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7fb2d77ca3201574b83cf8d8c6a70a5b4c507661
--- /dev/null
+++ b/spec/factories/lfs_objects.rb
@@ -0,0 +1,12 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :lfs_object do
+    oid "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80"
+    size 499013
+  end
+
+  trait :with_file do
+    file { fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png") }
+  end
+end
diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb
new file mode 100644
index 0000000000000000000000000000000000000000..93de6607df84a5fbfc4f977b56aabea1fcaec288
--- /dev/null
+++ b/spec/factories/lfs_objects_projects.rb
@@ -0,0 +1,8 @@
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :lfs_objects_project do
+    lfs_object
+    project
+  end
+end
diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb
index 6080d0ccdefcc10e3b599372a1ab02ef4abec480..729a49c9f7215326425e75a4649323fa480b7ff9 100644
--- a/spec/factories/merge_requests.rb
+++ b/spec/factories/merge_requests.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 FactoryGirl.define do
diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb
index 80d6bbee6c75ad952decf928fc574ff913d8815f..43d09b175347046eaa599c58a0550295874863a8 100644
--- a/spec/factories/releases.rb
+++ b/spec/factories/releases.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
 # Read about factories at https://github.com/thoughtbot/factory_girl
 
 FactoryGirl.define do
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index c2c7364f6c51b59dd9d75182f2d3b85a3577ccb4..4570e4091284cea19d32166a21f45192814babef 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -87,13 +87,16 @@ describe "Admin::Users", feature: true  do
     end
 
     it "should call send mail" do
-      expect(Notify).to receive(:new_user_email)
+      expect_any_instance_of(NotificationService).to receive(:new_user)
 
       click_button "Create user"
     end
 
     it "should send valid email to user with email & password" do
-      click_button "Create user"
+      perform_enqueued_jobs do
+        click_button "Create user"
+      end
+
       user = User.find_by(username: 'bang')
       email = ActionMailer::Base.deliveries.last
       expect(email.subject).to have_content('Account was created')
@@ -111,24 +114,60 @@ describe "Admin::Users", feature: true  do
       expect(page).to have_content(@user.name)
     end
 
-    describe 'Login as another user' do
-      it 'should show login button for other users and check that it works' do
-        another_user = create(:user)
+    describe 'Impersonation' do
+      let(:another_user) { create(:user) }
+      before { visit admin_user_path(another_user) }
+
+      context 'before impersonating' do
+        it 'shows impersonate button for other users' do
+          expect(page).to have_content('Impersonate')
+        end
+
+        it 'should not show impersonate button for admin itself' do
+          visit admin_user_path(@user)
+
+          expect(page).not_to have_content('Impersonate')
+        end
 
-        visit admin_user_path(another_user)
+        it 'should not show impersonate button for blocked user' do
+          another_user.block
 
-        click_link 'Log in as this user'
+          visit admin_user_path(another_user)
 
-        expect(page).to have_content("Logged in as #{another_user.username}")
+          expect(page).not_to have_content('Impersonate')
 
-        page.within '.sidebar-user .username' do
-          expect(page).to have_content(another_user.username)
+          another_user.activate
         end
       end
 
-      it 'should not show login button for admin itself' do
-        visit admin_user_path(@user)
-        expect(page).not_to have_content('Log in as this user')
+      context 'when impersonating' do
+        before { click_link 'Impersonate' }
+
+        it 'logs in as the user when impersonate is clicked' do
+          page.within '.sidebar-user .username' do
+            expect(page).to have_content(another_user.username)
+          end
+        end
+
+        it 'sees impersonation log out icon' do
+          icon = first('.fa.fa-user-secret')
+
+          expect(icon).to_not eql nil
+        end
+
+        it 'can log out of impersonated user back to original user' do
+          find(:css, 'li.impersonation a').click
+
+          page.within '.sidebar-user .username' do
+            expect(page).to have_content(@user.username)
+          end
+        end
+
+        it 'is redirected back to the impersonated users page in the admin after stopping' do
+          find(:css, 'li.impersonation a').click
+
+          expect(current_path).to eql "/admin/users/#{another_user.username}"
+        end
       end
     end
 
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 06adb7633b28b0ae7e63dbfd9f292a76ab19c07c..b025902663068080afd7cc28ed6a320ca979c96f 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -14,15 +14,25 @@ describe "Runners" do
       @project2 = FactoryGirl.create :ci_project
       @project2.gl_project.team << [user, :master]
 
+      @project3 = FactoryGirl.create :ci_project
+      @project3.gl_project.team << [user, :developer]
+
       @shared_runner = FactoryGirl.create :ci_shared_runner
       @specific_runner = FactoryGirl.create :ci_specific_runner
       @specific_runner2 = FactoryGirl.create :ci_specific_runner
+      @specific_runner3 = FactoryGirl.create :ci_specific_runner
       @project.runners << @specific_runner
       @project2.runners << @specific_runner2
+      @project3.runners << @specific_runner3
 
       visit runners_path(@project.gl_project)
     end
 
+    before do
+      expect(page).to_not have_content(@specific_runner3.display_name)
+      expect(page).to_not have_content(@specific_runner3.display_name)
+    end
+
     it "places runners in right places" do
       expect(page.find(".available-specific-runners")).to have_content(@specific_runner2.display_name)
       expect(page.find(".activated-specific-runners")).to have_content(@specific_runner.display_name)
@@ -76,10 +86,10 @@ describe "Runners" do
       @project.gl_project.team << [user, :master]
       @specific_runner = FactoryGirl.create :ci_specific_runner
       @project.runners << @specific_runner
-      visit runners_path(@project.gl_project)
     end
 
     it "shows runner information" do
+      visit runners_path(@project.gl_project)
       click_on @specific_runner.short_sha
       expect(page).to have_content(@specific_runner.platform)
     end
diff --git a/spec/finders/contributed_projects_finder_spec.rb b/spec/finders/contributed_projects_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..65d7f14c72143258e9cc634dbc8bc555e43ced49
--- /dev/null
+++ b/spec/finders/contributed_projects_finder_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe ContributedProjectsFinder do
+  let(:source_user) { create(:user) }
+  let(:current_user) { create(:user) }
+
+  let(:finder) { described_class.new(source_user) }
+
+  let!(:public_project) { create(:project, :public) }
+  let!(:private_project) { create(:project, :private) }
+
+  before do
+    private_project.team << [source_user, Gitlab::Access::MASTER]
+    private_project.team << [current_user, Gitlab::Access::DEVELOPER]
+    public_project.team << [source_user, Gitlab::Access::MASTER]
+
+    create(:event, action: Event::PUSHED, project: public_project,
+                   target: public_project, author: source_user)
+
+    create(:event, action: Event::PUSHED, project: private_project,
+                   target: private_project, author: source_user)
+  end
+
+  describe 'without a current user' do
+    subject { finder.execute }
+
+    it { is_expected.to eq([public_project]) }
+  end
+
+  describe 'with a current user' do
+    subject { finder.execute(current_user) }
+
+    it { is_expected.to eq([private_project, public_project]) }
+  end
+end
diff --git a/spec/finders/group_finder_spec.rb b/spec/finders/group_finder_spec.rb
deleted file mode 100644
index 78dc027837cf571b23a04e8344c3c168240991a5..0000000000000000000000000000000000000000
--- a/spec/finders/group_finder_spec.rb
+++ /dev/null
@@ -1,15 +0,0 @@
-require 'spec_helper'
-
-describe GroupsFinder do
-  let(:user) { create :user }
-  let!(:group) { create :group }
-  let!(:public_group) { create :group, public: true }
-  
-  describe :execute do
-    it 'finds public group' do
-      groups = GroupsFinder.new.execute(user)
-      expect(groups.size).to eq(1)
-      expect(groups.first).to eq(public_group)
-    end
-  end
-end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4f6a000822e6eae5c299262d58767763720c84be
--- /dev/null
+++ b/spec/finders/groups_finder_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe GroupsFinder do
+  describe '#execute' do
+    let(:user) { create(:user) }
+
+    let(:group1) { create(:group) }
+    let(:group2) { create(:group) }
+    let(:group3) { create(:group) }
+    let(:group4) { create(:group, public: true) }
+
+    let!(:public_project)   { create(:project, :public, group: group1) }
+    let!(:internal_project) { create(:project, :internal, group: group2) }
+    let!(:private_project)  { create(:project, :private, group: group3) }
+
+    let(:finder) { described_class.new }
+
+    describe 'with a user' do
+      subject { finder.execute(user) }
+
+      describe 'when the user is not a member of any groups' do
+        it { is_expected.to eq([group4, group2, group1]) }
+      end
+
+      describe 'when the user is a member of a group' do
+        before do
+          group3.add_user(user, Gitlab::Access::DEVELOPER)
+        end
+
+        it { is_expected.to eq([group4, group3, group2, group1]) }
+      end
+
+      describe 'when the user is a member of a private project' do
+        before do
+          private_project.team.add_user(user, Gitlab::Access::DEVELOPER)
+        end
+
+        it { is_expected.to eq([group4, group3, group2, group1]) }
+      end
+    end
+
+    describe 'without a user' do
+      subject { finder.execute }
+
+      it { is_expected.to eq([group4, group1]) }
+    end
+  end
+end
diff --git a/spec/finders/joined_groups_finder_spec.rb b/spec/finders/joined_groups_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2d9068cc720efd8f12a78b1a3ed3243d27dc19e7
--- /dev/null
+++ b/spec/finders/joined_groups_finder_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe JoinedGroupsFinder do
+  describe '#execute' do
+    let(:source_user) { create(:user) }
+    let(:current_user) { create(:user) }
+
+    let(:group1) { create(:group) }
+    let(:group2) { create(:group) }
+    let(:group3) { create(:group) }
+    let(:group4) { create(:group, public: true) }
+
+    let!(:public_project)   { create(:project, :public, group: group1) }
+    let!(:internal_project) { create(:project, :internal, group: group2) }
+    let!(:private_project)  { create(:project, :private, group: group3) }
+
+    let(:finder) { described_class.new(source_user) }
+
+    before do
+      [group1, group2, group3, group4].each do |group|
+        group.add_user(source_user, Gitlab::Access::MASTER)
+      end
+    end
+
+    describe 'with a current user' do
+      describe 'when the current user has access to the projects of the source user' do
+        before do
+          private_project.team.add_user(current_user, Gitlab::Access::DEVELOPER)
+        end
+
+        subject { finder.execute(current_user) }
+
+        it { is_expected.to eq([group4, group3, group2, group1]) }
+      end
+
+      describe 'when the current user does not have access to the projects of the source user' do
+        subject { finder.execute(current_user) }
+
+        it { is_expected.to eq([group4, group2, group1]) }
+      end
+    end
+
+    describe 'without a current user' do
+      subject { finder.execute }
+
+      it { is_expected.to eq([group4, group1]) }
+    end
+  end
+end
diff --git a/spec/finders/personal_projects_finder_spec.rb b/spec/finders/personal_projects_finder_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..38817add45684906a556bdcaec283486345f446a
--- /dev/null
+++ b/spec/finders/personal_projects_finder_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe PersonalProjectsFinder do
+  let(:source_user) { create(:user) }
+  let(:current_user) { create(:user) }
+
+  let(:finder) { described_class.new(source_user) }
+
+  let!(:public_project) do
+    create(:project, :public, namespace: source_user.namespace, name: 'A',
+                              path: 'A')
+  end
+
+  let!(:private_project) do
+    create(:project, :private, namespace: source_user.namespace, name: 'B',
+                               path: 'B')
+  end
+
+  before do
+    private_project.team << [current_user, Gitlab::Access::DEVELOPER]
+  end
+
+  describe 'without a current user' do
+    subject { finder.execute }
+
+    it { is_expected.to eq([public_project]) }
+  end
+
+  describe 'with a current user' do
+    subject { finder.execute(current_user) }
+
+    it { is_expected.to eq([private_project, public_project]) }
+  end
+end
diff --git a/spec/finders/projects_finder_spec.rb b/spec/finders/projects_finder_spec.rb
index de9d4cd6128c020112ea151dc2f161d0c84306f2..f32641ef0f6b9e2f8362868860892d4ccdcb8b2e 100644
--- a/spec/finders/projects_finder_spec.rb
+++ b/spec/finders/projects_finder_spec.rb
@@ -1,51 +1,63 @@
 require 'spec_helper'
 
 describe ProjectsFinder do
-  let(:user) { create :user }
-  let(:group) { create :group }
+  describe '#execute' do
+    let(:user) { create(:user) }
+    let(:group) { create(:group) }
 
-  let(:project1) { create(:empty_project, :public,   group: group) }
-  let(:project2) { create(:empty_project, :internal, group: group) }
-  let(:project3) { create(:empty_project, :private,  group: group) }
-  let(:project4) { create(:empty_project, :private,  group: group) }
+    let!(:private_project) do
+      create(:project, :private, name: 'A', path: 'A')
+    end
 
-  context 'non authenticated' do
-    subject { ProjectsFinder.new.execute(nil, group: group) }
+    let!(:internal_project) do
+      create(:project, :internal, group: group, name: 'B', path: 'B')
+    end
 
-    it { is_expected.to include(project1) }
-    it { is_expected.not_to include(project2) }
-    it { is_expected.not_to include(project3) }
-    it { is_expected.not_to include(project4) }
-  end
+    let!(:public_project) do
+      create(:project, :public, group: group, name: 'C', path: 'C')
+    end
 
-  context 'authenticated' do
-    subject { ProjectsFinder.new.execute(user, group: group) }
+    let(:finder) { described_class.new }
 
-    it { is_expected.to include(project1) }
-    it { is_expected.to include(project2) }
-    it { is_expected.not_to include(project3) }
-    it { is_expected.not_to include(project4) }
-  end
+    describe 'without a group' do
+      describe 'without a user' do
+        subject { finder.execute }
 
-  context 'authenticated, project member' do
-    before { project3.team << [user, :developer] }
+        it { is_expected.to eq([public_project]) }
+      end
 
-    subject { ProjectsFinder.new.execute(user, group: group) }
+      describe 'with a user' do
+        subject { finder.execute(user) }
 
-    it { is_expected.to include(project1) }
-    it { is_expected.to include(project2) }
-    it { is_expected.to include(project3) }
-    it { is_expected.not_to include(project4) }
-  end
+        describe 'without private projects' do
+          it { is_expected.to eq([public_project, internal_project]) }
+        end
+
+        describe 'with private projects' do
+          before do
+            private_project.team.add_user(user, Gitlab::Access::MASTER)
+          end
+
+          it do
+            is_expected.to eq([public_project, internal_project,
+                               private_project])
+          end
+        end
+      end
+    end
+
+    describe 'with a group' do
+      describe 'without a user' do
+        subject { finder.execute(nil, group: group) }
 
-  context 'authenticated, group member' do
-    before { group.add_developer(user) }
+        it { is_expected.to eq([public_project]) }
+      end
 
-    subject { ProjectsFinder.new.execute(user, group: group) }
+      describe 'with a user' do
+        subject { finder.execute(user, group: group) }
 
-    it { is_expected.to include(project1) }
-    it { is_expected.to include(project2) }
-    it { is_expected.to include(project3) }
-    it { is_expected.to include(project4) }
+        it { is_expected.to eq([public_project, internal_project]) }
+      end
+    end
   end
 end
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 1dfae0fbd3fea83a4f0966f20f27d5cfbeb880f2..0a64b70d6a67af089caf180d3f671d1b3f4e0a5d 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -59,7 +59,7 @@ describe ApplicationHelper do
 
       avatar_url = "http://localhost/uploads/project/avatar/#{project.id}/banana_sample.gif"
       expect(helper.project_icon("#{project.namespace.to_param}/#{project.to_param}").to_s).
-        to eq "<img alt=\"Banana sample\" src=\"#{avatar_url}\" />"
+        to eq "<img src=\"#{avatar_url}\" alt=\"Banana sample\" />"
     end
 
     it 'should give uploaded icon when present' do
@@ -95,9 +95,9 @@ describe ApplicationHelper do
     end
 
     it 'should call gravatar_icon when no User exists with the given email' do
-      expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20)
+      expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
 
-      helper.avatar_icon('foo@example.com', 20)
+      helper.avatar_icon('foo@example.com', 20, 2)
     end
 
     describe 'using a User' do
@@ -150,15 +150,19 @@ describe ApplicationHelper do
         stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
 
         expect(gravatar_icon(user_email, 20)).
-          to eq('http://example.local/?s=20&hash=b58c6f14d292556214bd64909bcdb118')
+          to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
       end
 
       it 'accepts a custom size argument' do
-        expect(helper.gravatar_icon(user_email, 64)).to include '?s=64'
+        expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
       end
 
-      it 'defaults size to 40 when given an invalid size' do
-        expect(helper.gravatar_icon(user_email, nil)).to include '?s=40'
+      it 'defaults size to 40@2x when given an invalid size' do
+        expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
+      end
+
+      it 'accepts a scaling factor' do
+        expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
       end
 
       it 'ignores case and surrounding whitespace' do
diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb
index 78a6b631eb25cb059abd0a8dfe21fc9e8a885560..1f2c4ee77b59fdf79c94f5295f3c68048f5136c3 100644
--- a/spec/helpers/issues_helper_spec.rb
+++ b/spec/helpers/issues_helper_spec.rb
@@ -127,4 +127,30 @@ describe IssuesHelper do
     it { is_expected.to eq("!1, !2, or !3") }
   end
 
+  describe "#url_to_emoji" do
+    it "returns url" do
+      expect(url_to_emoji("smile")).to include("emoji/1F604.png")
+    end
+  end
+
+  describe "#emoji_list" do
+    it "returns url" do
+      expect(emoji_list).to be_kind_of(Array)
+    end
+  end
+
+  describe "#note_active_class" do
+    before do
+      @note = create :note
+      @note1 = create :note
+    end
+
+    it "returns empty string for unauthenticated user" do
+      expect(note_active_class(Note.all, nil)).to eq("")
+    end
+
+    it "returns active string for author" do
+      expect(note_active_class(Note.all, @note.author)).to eq("active")
+    end
+  end
 end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index 29fc271382113d8138b1a1575b3c7f65c91442a9..6f287719ba61c84c519b566b5ede3e685a6b7fe6 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -333,6 +333,60 @@ module Ci
       end
     end
 
+    describe "Caches" do
+      it "returns cache when defined globally" do
+        config = YAML.dump({
+                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             rspec: {
+                               script: "rspec"
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
+          paths: ["logs/", "binaries/"],
+          untracked: true,
+        )
+      end
+
+      it "returns cache when defined in a job" do
+        config = YAML.dump({
+                             rspec: {
+                               cache: { paths: ["logs/", "binaries/"], untracked: true },
+                               script: "rspec"
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
+          paths: ["logs/", "binaries/"],
+          untracked: true,
+        )
+      end
+
+      it "overwrite cache when defined for a job and globally" do
+        config = YAML.dump({
+                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             rspec: {
+                               script: "rspec",
+                               cache: { paths: ["test/"], untracked: false },
+                             }
+                           })
+
+        config_processor = GitlabCiYamlProcessor.new(config)
+
+        expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(1)
+        expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
+          paths: ["test/"],
+          untracked: false,
+        )
+      end
+    end
+
     describe "Artifacts" do
       it "returns artifacts when defined" do
         config = YAML.dump({
@@ -371,8 +425,12 @@ module Ci
     end
 
     describe "Error handling" do
+      it "fails to parse YAML" do
+        expect{GitlabCiYamlProcessor.new("invalid: yaml: test")}.to raise_error(Psych::SyntaxError)
+      end
+
       it "indicates that object is invalid" do
-        expect{GitlabCiYamlProcessor.new("invalid_yaml\n!ccdvlf%612334@@@@")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
+        expect{GitlabCiYamlProcessor.new("invalid_yaml")}.to raise_error(GitlabCiYamlProcessor::ValidationError)
       end
 
       it "returns errors if tags parameter is invalid" do
@@ -542,6 +600,34 @@ module Ci
           GitlabCiYamlProcessor.new(config)
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: artifacts:paths parameter should be an array of strings")
       end
+
+      it "returns errors if cache:untracked is not an array of strings" do
+        config = YAML.dump({ cache: { untracked: "string" }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:untracked parameter should be an boolean")
+      end
+
+      it "returns errors if cache:paths is not an array of strings" do
+        config = YAML.dump({ cache: { paths: "string" }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings")
+      end
+
+      it "returns errors if job cache:untracked is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { untracked: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:untracked parameter should be an boolean")
+      end
+
+      it "returns errors if job cache:paths is not an array of strings" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { paths: "string" } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:paths parameter should be an array of strings")
+      end
     end
   end
 end
diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/inline_diff_spec.rb
similarity index 100%
rename from spec/lib/gitlab/diff/inline_diff_spec.rb
rename to spec/lib/gitlab/inline_diff_spec.rb
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5b13d65c7c06011e1846709fd64e16ceb3b5a412
--- /dev/null
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -0,0 +1,765 @@
+require 'spec_helper'
+
+describe Gitlab::Lfs::Router do
+  let(:project) { create(:project) }
+  let(:public_project) { create(:project, :public) }
+  let(:forked_project) { fork_project(public_project, user) }
+
+  let(:user) { create(:user) }
+  let(:user_two) { create(:user) }
+  let!(:lfs_object) { create(:lfs_object, :with_file) }
+
+  let(:request) { Rack::Request.new(env) }
+  let(:env) do
+    {
+      'rack.input'     => '',
+      'REQUEST_METHOD' => 'GET',
+    }
+  end
+
+  let(:lfs_router_auth) { new_lfs_router(project, user) }
+  let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+  let(:lfs_router_public_auth) { new_lfs_router(public_project, user) }
+  let(:lfs_router_public_noauth) { new_lfs_router(public_project, nil) }
+  let(:lfs_router_forked_noauth) { new_lfs_router(forked_project, nil) }
+  let(:lfs_router_forked_auth) { new_lfs_router(forked_project, user_two) }
+
+  let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
+  let(:sample_size) { 499013 }
+  let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+  let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+
+  describe 'when lfs is disabled' do
+    before do
+      allow(Gitlab.config.lfs).to receive(:enabled).and_return(false)
+      env['REQUEST_METHOD'] = 'POST'
+      body = {
+                'objects' => [
+                  { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                    'size' => 1575078
+                  },
+                  { 'oid' => sample_oid,
+                    'size' => sample_size
+                  }
+                ],
+                'operation' => 'upload'
+              }.to_json
+      env['rack.input'] = StringIO.new(body)
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
+    end
+
+    it 'responds with 501' do
+      expect(lfs_router_auth.try_call).to match_array(respond_with_disabled)
+    end
+  end
+
+  describe 'when fetching lfs object using deprecated API' do
+    before do
+      enable_lfs
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/#{sample_oid}"
+    end
+
+    it 'responds with 501' do
+      expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
+    end
+  end
+
+  describe 'when fetching lfs object' do
+    before do
+      enable_lfs
+      env['HTTP_ACCEPT'] = "application/vnd.git-lfs+json; charset=utf-8"
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}"
+    end
+
+    describe 'and request comes from gitlab-workhorse' do
+      context 'without user being authorized' do
+        it "responds with status 401" do
+          expect(lfs_router_noauth.try_call.first).to eq(401)
+        end
+      end
+
+      context 'with required headers' do
+        before do
+          env['HTTP_X_SENDFILE_TYPE'] = "X-Sendfile"
+        end
+
+        context 'when user does not have project access' do
+          it "responds with status 403" do
+            expect(lfs_router_auth.try_call.first).to eq(403)
+          end
+        end
+
+        context 'when user has project access' do
+          before do
+            project.lfs_objects << lfs_object
+            project.team << [user, :master]
+          end
+
+          it "responds with status 200" do
+            expect(lfs_router_auth.try_call.first).to eq(200)
+          end
+
+          it "responds with the file location" do
+            expect(lfs_router_auth.try_call[1]['Content-Type']).to eq("application/octet-stream")
+            expect(lfs_router_auth.try_call[1]['X-Sendfile']).to eq(lfs_object.file.path)
+          end
+        end
+      end
+
+      context 'without required headers' do
+        it "responds with status 403" do
+          expect(lfs_router_auth.try_call.first).to eq(403)
+        end
+      end
+    end
+  end
+
+  describe 'when handling lfs request using deprecated API' do
+    before do
+      enable_lfs
+      env['REQUEST_METHOD'] = 'POST'
+      env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/info/lfs/objects"
+    end
+
+    it 'responds with 501' do
+      expect(lfs_router_auth.try_call).to match_array(respond_with_deprecated)
+    end
+  end
+
+  describe 'when handling lfs batch request' do
+    before do
+      enable_lfs
+      env['REQUEST_METHOD'] = 'POST'
+      env['PATH_INFO'] = "#{project.repository.path_with_namespace}.git/info/lfs/objects/batch"
+    end
+
+    describe 'download' do
+      describe 'when user is authenticated' do
+        before do
+          body = { 'operation' => 'download',
+                   'objects' => [
+                     { 'oid' => sample_oid,
+                       'size' => sample_size
+                     }]
+          }.to_json
+          env['rack.input'] = StringIO.new(body)
+        end
+
+        describe 'when user has download access' do
+          before do
+            @auth = authorize(user)
+            env["HTTP_AUTHORIZATION"] = @auth
+            project.team << [user, :reporter]
+          end
+
+          context 'when downloading an lfs object that is assigned to our project' do
+            before do
+              project.lfs_objects << lfs_object
+            end
+
+            it 'responds with status 200 and href to download' do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+
+              expect(response_body).to eq('objects' => [
+                { 'oid' => sample_oid,
+                  'size' => sample_size,
+                  'actions' => {
+                    'download' => {
+                      'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                      'header' => { 'Authorization' => @auth }
+                    }
+                  }
+                }])
+            end
+          end
+
+          context 'when downloading an lfs object that is assigned to other project' do
+            before do
+              public_project.lfs_objects << lfs_object
+            end
+
+            it 'responds with status 200 and error message' do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+
+              expect(response_body).to eq('objects' => [
+                { 'oid' => sample_oid,
+                  'size' => sample_size,
+                  'error' => {
+                    'code' => 404,
+                    'message' => "Object does not exist on the server or you don't have permissions to access it",
+                  }
+                }])
+            end
+          end
+
+          context 'when downloading a lfs object that does not exist' do
+            before do
+              body = { 'operation' => 'download',
+                       'objects' => [
+                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                           'size' => 1575078
+                         }]
+              }.to_json
+              env['rack.input'] = StringIO.new(body)
+            end
+
+            it "responds with status 200 and error message" do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+
+              expect(response_body).to eq('objects' => [
+                { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                  'size' => 1575078,
+                  'error' => {
+                    'code' => 404,
+                    'message' => "Object does not exist on the server or you don't have permissions to access it",
+                  }
+                }])
+            end
+          end
+
+          context 'when downloading one new and one existing lfs object' do
+            before do
+              body = { 'operation' => 'download',
+                       'objects' => [
+                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                           'size' => 1575078
+                         },
+                         { 'oid' => sample_oid,
+                           'size' => sample_size
+                         }
+                       ]
+              }.to_json
+              env['rack.input'] = StringIO.new(body)
+              project.lfs_objects << lfs_object
+            end
+
+            it "responds with status 200 with upload hypermedia link for the new object" do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+
+              expect(response_body).to eq('objects' => [
+                { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                  'size' => 1575078,
+                  'error' => {
+                    'code' => 404,
+                    'message' => "Object does not exist on the server or you don't have permissions to access it",
+                  }
+                },
+                { 'oid' => sample_oid,
+                  'size' => sample_size,
+                  'actions' => {
+                    'download' => {
+                      'href' => "#{project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                      'header' => { 'Authorization' => @auth }
+                    }
+                  }
+                }])
+            end
+          end
+        end
+
+        context 'when user does is not member of the project' do
+          before do
+            @auth = authorize(user)
+            env["HTTP_AUTHORIZATION"] = @auth
+            project.team << [user, :guest]
+          end
+
+          it 'responds with 403' do
+            expect(lfs_router_auth.try_call.first).to eq(403)
+          end
+        end
+
+        context 'when user does not have download access' do
+          before do
+            @auth = authorize(user)
+            env["HTTP_AUTHORIZATION"] = @auth
+            project.team << [user, :guest]
+          end
+
+          it 'responds with 403' do
+            expect(lfs_router_auth.try_call.first).to eq(403)
+          end
+        end
+      end
+
+      context 'when user is not authenticated' do
+        before do
+          body = { 'operation' => 'download',
+                   'objects' => [
+                     { 'oid' => sample_oid,
+                       'size' => sample_size
+                     }],
+
+          }.to_json
+          env['rack.input'] = StringIO.new(body)
+        end
+
+        describe 'is accessing public project' do
+          before do
+            public_project.lfs_objects << lfs_object
+          end
+
+          it 'responds with status 200 and href to download' do
+            response = lfs_router_public_noauth.try_call
+            expect(response.first).to eq(200)
+            response_body = ActiveSupport::JSON.decode(response.last.first)
+
+            expect(response_body).to eq('objects' => [
+              { 'oid' => sample_oid,
+                'size' => sample_size,
+                'actions' => {
+                  'download' => {
+                    'href' => "#{public_project.http_url_to_repo}/gitlab-lfs/objects/#{sample_oid}",
+                    'header' => {}
+                  }
+                }
+              }])
+          end
+        end
+
+        describe 'is accessing non-public project' do
+          before do
+            project.lfs_objects << lfs_object
+          end
+
+          it 'responds with authorization required' do
+            expect(lfs_router_noauth.try_call.first).to eq(401)
+          end
+        end
+      end
+    end
+
+    describe 'upload' do
+      describe 'when user is authenticated' do
+        before do
+          body = { 'operation' => 'upload',
+                   'objects' => [
+                     { 'oid' => sample_oid,
+                       'size' => sample_size
+                     }]
+          }.to_json
+          env['rack.input'] = StringIO.new(body)
+        end
+
+        describe 'when user has project push access' do
+          before do
+            @auth = authorize(user)
+            env["HTTP_AUTHORIZATION"] = @auth
+            project.team << [user, :developer]
+          end
+
+          context 'when pushing an lfs object that already exists' do
+            before do
+              public_project.lfs_objects << lfs_object
+            end
+
+            it "responds with status 200 and links the object to the project" do
+              response_body = lfs_router_auth.try_call.last
+              response = ActiveSupport::JSON.decode(response_body.first)
+
+              expect(response['objects']).to be_kind_of(Array)
+              expect(response['objects'].first['oid']).to eq(sample_oid)
+              expect(response['objects'].first['size']).to eq(sample_size)
+              expect(lfs_object.projects.pluck(:id)).to_not include(project.id)
+              expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
+              expect(response['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}")
+              expect(response['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
+            end
+          end
+
+          context 'when pushing a lfs object that does not exist' do
+            before do
+              body = { 'operation' => 'upload',
+                       'objects' => [
+                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                           'size' => 1575078
+                         }]
+              }.to_json
+              env['rack.input'] = StringIO.new(body)
+            end
+
+            it "responds with status 200 and upload hypermedia link" do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+              expect(response_body['objects']).to be_kind_of(Array)
+              expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+              expect(response_body['objects'].first['size']).to eq(1575078)
+              expect(lfs_object.projects.pluck(:id)).not_to include(project.id)
+              expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+              expect(response_body['objects'].first['actions']['upload']['header']).to eq('Authorization' => @auth)
+            end
+          end
+
+          context 'when pushing one new and one existing lfs object' do
+            before do
+              body = { 'operation' => 'upload',
+                       'objects' => [
+                         { 'oid' => '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897',
+                           'size' => 1575078
+                         },
+                         { 'oid' => sample_oid,
+                           'size' => sample_size
+                         }
+                       ]
+              }.to_json
+              env['rack.input'] = StringIO.new(body)
+              project.lfs_objects << lfs_object
+            end
+
+            it "responds with status 200 with upload hypermedia link for the new object" do
+              response = lfs_router_auth.try_call
+              expect(response.first).to eq(200)
+
+              response_body = ActiveSupport::JSON.decode(response.last.first)
+              expect(response_body['objects']).to be_kind_of(Array)
+
+              expect(response_body['objects'].first['oid']).to eq("91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897")
+              expect(response_body['objects'].first['size']).to eq(1575078)
+              expect(response_body['objects'].first['actions']['upload']['href']).to eq("#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}.git/gitlab-lfs/objects/91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897/1575078")
+              expect(response_body['objects'].first['actions']['upload']['header']).to eq("Authorization" => @auth)
+
+              expect(response_body['objects'].last['oid']).to eq(sample_oid)
+              expect(response_body['objects'].last['size']).to eq(sample_size)
+              expect(response_body['objects'].last).to_not have_key('actions')
+            end
+          end
+        end
+
+        context 'when user does not have push access' do
+          it 'responds with 403' do
+            expect(lfs_router_auth.try_call.first).to eq(403)
+          end
+        end
+      end
+
+      context 'when user is not authenticated' do
+        before do
+          env['rack.input'] = StringIO.new(
+            { 'objects' => [], 'operation' => 'upload' }.to_json
+          )
+        end
+
+        context 'when user has push access' do
+          before do
+            project.team << [user, :master]
+          end
+
+          it "responds with status 401" do
+            expect(lfs_router_public_noauth.try_call.first).to eq(401)
+          end
+        end
+
+        context 'when user does not have push access' do
+          it "responds with status 401" do
+            expect(lfs_router_public_noauth.try_call.first).to eq(401)
+          end
+        end
+      end
+    end
+
+    describe 'unsupported' do
+      before do
+        body = { 'operation' => 'other',
+                 'objects' => [
+                   { 'oid' => sample_oid,
+                     'size' => sample_size
+                   }]
+        }.to_json
+        env['rack.input'] = StringIO.new(body)
+      end
+
+      it 'responds with status 404' do
+        expect(lfs_router_public_noauth.try_call.first).to eq(404)
+      end
+    end
+  end
+
+  describe 'when pushing a lfs object' do
+    before do
+      enable_lfs
+      env['REQUEST_METHOD'] = 'PUT'
+    end
+
+    describe 'to one project' do
+      describe 'when user has push access to the project' do
+        before do
+          project.team << [user, :master]
+        end
+
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with status 200, location of lfs store and object details' do
+              json_response = ActiveSupport::JSON.decode(lfs_router_auth.try_call.last.first)
+
+              expect(lfs_router_auth.try_call.first).to eq(200)
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with status 200 and lfs object is linked to the project' do
+              expect(lfs_router_auth.try_call.first).to eq(200)
+              expect(lfs_object.projects.pluck(:id)).to include(project.id)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent with a malformed headers' do
+            before do
+              env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
+              env["HTTP_X_GITLAB_LFS_TMP"] = "cat /etc/passwd"
+            end
+
+            it 'does not recognize it as a valid lfs command' do
+              expect(lfs_router_noauth.try_call).to eq(nil)
+            end
+          end
+        end
+      end
+
+      describe 'and user does not have push access' do
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_auth.try_call.first).to eq(403)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_auth.try_call.first).to eq(403)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          let(:lfs_router_noauth) { new_lfs_router(project, nil) }
+
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+    end
+
+    describe "to a forked project" do
+      let(:forked_project) { fork_project(public_project, user) }
+
+      describe 'when user has push access to the project' do
+        before do
+          forked_project.team << [user_two, :master]
+        end
+
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with status 200, location of lfs store and object details' do
+              json_response = ActiveSupport::JSON.decode(lfs_router_forked_auth.try_call.last.first)
+
+              expect(lfs_router_forked_auth.try_call.first).to eq(200)
+              expect(json_response['StoreLFSPath']).to eq("#{Gitlab.config.shared.path}/lfs-objects/tmp/upload")
+              expect(json_response['LfsOid']).to eq(sample_oid)
+              expect(json_response['LfsSize']).to eq(sample_size)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with status 200 and lfs object is linked to the source project' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(200)
+              expect(lfs_object.projects.pluck(:id)).to include(public_project.id)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with status 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+
+      describe 'and user does not have push access' do
+        describe 'when user is authenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(403)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with 403' do
+              expect(lfs_router_forked_auth.try_call.first).to eq(403)
+            end
+          end
+        end
+
+        describe 'when user is unauthenticated' do
+          context 'and request is sent by gitlab-workhorse to authorize the request' do
+            before do
+              header_for_upload_authorize(forked_project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+
+          context 'and request is sent by gitlab-workhorse to finalize the upload' do
+            before do
+              headers_for_upload_finalize(forked_project)
+            end
+
+            it 'responds with 401' do
+              expect(lfs_router_forked_noauth.try_call.first).to eq(401)
+            end
+          end
+        end
+      end
+
+      describe 'and second project not related to fork or a source project' do
+        let(:second_project) { create(:project) }
+        let(:lfs_router_second_project) { new_lfs_router(second_project, user) }
+
+        before do
+          public_project.lfs_objects << lfs_object
+          headers_for_upload_finalize(second_project)
+        end
+
+        context 'when pushing the same lfs object to the second project' do
+          before do
+            second_project.team << [user, :master]
+          end
+
+          it 'responds with 200 and links the lfs object to the project' do
+            expect(lfs_router_second_project.try_call.first).to eq(200)
+            expect(lfs_object.projects.pluck(:id)).to include(second_project.id, public_project.id)
+          end
+        end
+      end
+    end
+  end
+
+  def enable_lfs
+    allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
+  end
+
+  def authorize(user)
+    ActionController::HttpAuthentication::Basic.encode_credentials(user.username, user.password)
+  end
+
+  def new_lfs_router(project, user)
+    Gitlab::Lfs::Router.new(project, user, request)
+  end
+
+  def header_for_upload_authorize(project)
+    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}/authorize"
+  end
+
+  def headers_for_upload_finalize(project)
+    env["PATH_INFO"] = "#{project.repository.path_with_namespace}.git/gitlab-lfs/objects/#{sample_oid}/#{sample_size}"
+    env["HTTP_X_GITLAB_LFS_TMP"] = "#{sample_oid}6e561c9d4"
+  end
+
+  def fork_project(project, user, object = nil)
+    allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
+    Projects::ForkService.new(project, user, {}).execute
+  end
+end
diff --git a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
index fc21b65a8437106d35f54ac484d0f4bdbda86cf8..ae286c8be2b2123b9bae9c88f5e6e8f4b3b20e60 100644
--- a/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
+++ b/spec/lib/gitlab/markdown/label_reference_filter_spec.rb
@@ -71,7 +71,7 @@ module Gitlab::Markdown
         doc = filter("See #{reference}")
 
         expect(doc.css('a').first.attr('href')).to eq urls.
-          namespace_project_issues_url(project.namespace, project, label_name: label.name)
+          namespace_project_issues_path(project.namespace, project, label_name: label.name)
       end
 
       it 'links with adjacent text' do
@@ -94,7 +94,7 @@ module Gitlab::Markdown
         doc = filter("See #{reference}")
 
         expect(doc.css('a').first.attr('href')).to eq urls.
-          namespace_project_issues_url(project.namespace, project, label_name: label.name)
+          namespace_project_issues_path(project.namespace, project, label_name: label.name)
         expect(doc.text).to eq 'See gfm'
       end
 
@@ -118,7 +118,7 @@ module Gitlab::Markdown
         doc = filter("See #{reference}")
 
         expect(doc.css('a').first.attr('href')).to eq urls.
-          namespace_project_issues_url(project.namespace, project, label_name: label.name)
+          namespace_project_issues_path(project.namespace, project, label_name: label.name)
         expect(doc.text).to eq 'See gfm references'
       end
 
diff --git a/spec/lib/gitlab/sherlock/transaction_spec.rb b/spec/lib/gitlab/sherlock/transaction_spec.rb
index bb49fb65cf8c6ec65bcb5f8a02abe5b17aed2f2b..fb80c62c79479018114b643e0b40b80a1cd97a38 100644
--- a/spec/lib/gitlab/sherlock/transaction_spec.rb
+++ b/spec/lib/gitlab/sherlock/transaction_spec.rb
@@ -84,6 +84,19 @@ describe Gitlab::Sherlock::Transaction do
     end
   end
 
+  describe '#query_duration' do
+    it 'returns the total query duration in seconds' do
+      time   = Time.now
+      query1 = Gitlab::Sherlock::Query.new('SELECT 1', time, time + 5)
+      query2 = Gitlab::Sherlock::Query.new('SELECT 2', time, time + 2)
+
+      transaction.queries << query1
+      transaction.queries << query2
+
+      expect(transaction.query_duration).to be_within(0.1).of(7.0)
+    end
+  end
+
   describe '#to_param' do
     it 'returns the transaction ID' do
       expect(transaction.to_param).to eq(transaction.id)
diff --git a/spec/lib/gitlab/sql/union_spec.rb b/spec/lib/gitlab/sql/union_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9e1cd4419e0b00bdc904f3279db285706a4ae07c
--- /dev/null
+++ b/spec/lib/gitlab/sql/union_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe Gitlab::SQL::Union do
+  describe '#to_sql' do
+    it 'returns a String joining relations together using a UNION' do
+      rel1  = User.where(email: 'alice@example.com')
+      rel2  = User.where(email: 'bob@example.com')
+      union = described_class.new([rel1, rel2])
+
+      sql1 = rel1.reorder(nil).to_sql
+      sql2 = rel2.reorder(nil).to_sql
+
+      expect(union.to_sql).to eq("#{sql1}\nUNION\n#{sql2}")
+    end
+  end
+end
diff --git a/spec/lib/votes_spec.rb b/spec/lib/votes_spec.rb
deleted file mode 100644
index 39e5d054e62057c6249e787455257a8020710ef8..0000000000000000000000000000000000000000
--- a/spec/lib/votes_spec.rb
+++ /dev/null
@@ -1,188 +0,0 @@
-require 'spec_helper'
-
-describe Issue, 'Votes' do
-  let(:issue) { create(:issue) }
-
-  describe "#upvotes" do
-    it "with no notes has a 0/0 score" do
-      expect(issue.upvotes).to eq(0)
-    end
-
-    it "should recognize non-+1 notes" do
-      add_note "No +1 here"
-      expect(issue.notes.size).to eq(1)
-      expect(issue.notes.first.upvote?).to be_falsey
-      expect(issue.upvotes).to eq(0)
-    end
-
-    it "should recognize a single +1 note" do
-      add_note "+1 This is awesome"
-      expect(issue.upvotes).to eq(1)
-    end
-
-    it 'should recognize multiple +1 notes' do
-      add_note '+1 This is awesome', create(:user)
-      add_note '+1 I want this', create(:user)
-      expect(issue.upvotes).to eq(2)
-    end
-
-    it 'should not count 2 +1 votes from the same user' do
-      add_note '+1 This is awesome'
-      add_note '+1 I want this'
-      expect(issue.upvotes).to eq(1)
-    end
-  end
-
-  describe "#downvotes" do
-    it "with no notes has a 0/0 score" do
-      expect(issue.downvotes).to eq(0)
-    end
-
-    it "should recognize non--1 notes" do
-      add_note "Almost got a -1"
-      expect(issue.notes.size).to eq(1)
-      expect(issue.notes.first.downvote?).to be_falsey
-      expect(issue.downvotes).to eq(0)
-    end
-
-    it "should recognize a single -1 note" do
-      add_note "-1 This is bad"
-      expect(issue.downvotes).to eq(1)
-    end
-
-    it "should recognize multiple -1 notes" do
-      add_note('-1 This is bad', create(:user))
-      add_note('-1 Away with this', create(:user))
-      expect(issue.downvotes).to eq(2)
-    end
-  end
-
-  describe "#votes_count" do
-    it "with no notes has a 0/0 score" do
-      expect(issue.votes_count).to eq(0)
-    end
-
-    it "should recognize non notes" do
-      add_note "No +1 here"
-      expect(issue.notes.size).to eq(1)
-      expect(issue.votes_count).to eq(0)
-    end
-
-    it "should recognize a single +1 note" do
-      add_note "+1 This is awesome"
-      expect(issue.votes_count).to eq(1)
-    end
-
-    it "should recognize a single -1 note" do
-      add_note "-1 This is bad"
-      expect(issue.votes_count).to eq(1)
-    end
-
-    it "should recognize multiple notes" do
-      add_note('+1 This is awesome', create(:user))
-      add_note('-1 This is bad', create(:user))
-      add_note('+1 I want this', create(:user))
-      expect(issue.votes_count).to eq(3)
-    end
-
-    it 'should not count 2 -1 votes from the same user' do
-      add_note '-1 This is suspicious'
-      add_note '-1 This is bad'
-      expect(issue.votes_count).to eq(1)
-    end
-  end
-
-  describe "#upvotes_in_percent" do
-    it "with no notes has a 0% score" do
-      expect(issue.upvotes_in_percent).to eq(0)
-    end
-
-    it "should count a single 1 note as 100%" do
-      add_note "+1 This is awesome"
-      expect(issue.upvotes_in_percent).to eq(100)
-    end
-
-    it 'should count multiple +1 notes as 100%' do
-      add_note('+1 This is awesome', create(:user))
-      add_note('+1 I want this', create(:user))
-      expect(issue.upvotes_in_percent).to eq(100)
-    end
-
-    it 'should count fractions for multiple +1 and -1 notes correctly' do
-      add_note('+1 This is awesome', create(:user))
-      add_note('+1 I want this', create(:user))
-      add_note('-1 This is bad', create(:user))
-      add_note('+1 me too', create(:user))
-      expect(issue.upvotes_in_percent).to eq(75)
-    end
-  end
-
-  describe "#downvotes_in_percent" do
-    it "with no notes has a 0% score" do
-      expect(issue.downvotes_in_percent).to eq(0)
-    end
-
-    it "should count a single -1 note as 100%" do
-      add_note "-1 This is bad"
-      expect(issue.downvotes_in_percent).to eq(100)
-    end
-
-    it 'should count multiple -1 notes as 100%' do
-      add_note('-1 This is bad', create(:user))
-      add_note('-1 Away with this', create(:user))
-      expect(issue.downvotes_in_percent).to eq(100)
-    end
-
-    it 'should count fractions for multiple +1 and -1 notes correctly' do
-      add_note('+1 This is awesome', create(:user))
-      add_note('+1 I want this', create(:user))
-      add_note('-1 This is bad', create(:user))
-      add_note('+1 me too', create(:user))
-      expect(issue.downvotes_in_percent).to eq(25)
-    end
-  end
-
-  describe '#filter_superceded_votes' do
-
-    it 'should count a users vote only once amongst multiple votes' do
-      add_note('-1 This needs work before I will accept it')
-      add_note('+1 I want this', create(:user))
-      add_note('+1 This is is awesome', create(:user))
-      add_note('+1 this looks good now')
-      add_note('+1 This is awesome', create(:user))
-      add_note('+1 me too', create(:user))
-      expect(issue.downvotes).to eq(0)
-      expect(issue.upvotes).to eq(5)
-    end
-
-    it 'should count each users vote only once' do
-      add_note '-1 This needs work before it will be accepted'
-      add_note '+1 I like this'
-      add_note '+1 I still like this'
-      add_note '+1 I really like this'
-      add_note '+1 Give me this now!!!!'
-      expect(issue.downvotes).to eq(0)
-      expect(issue.upvotes).to eq(1)
-    end
-
-    it 'should count a users vote only once without caring about comments' do
-      add_note '-1 This needs work before it will be accepted'
-      add_note 'Comment 1'
-      add_note 'Another comment'
-      add_note '+1 vote'
-      add_note 'final comment'
-      expect(issue.downvotes).to eq(0)
-      expect(issue.upvotes).to eq(1)
-    end
-
-  end
-
-  def add_note(text, author = issue.author)
-    created_at = Time.now - 1.hour + Note.count.seconds
-    issue.notes << create(:note,
-                          note: text,
-                          project: issue.project,
-                          author_id: author.id,
-                          created_at: created_at)
-  end
-end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 47863d54579e05959f2c5930d89bf2352d8f695e..27e509933b2a0b1a4ef0c5d05fbdf86bc2578d72 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -77,6 +77,32 @@ describe Notify do
     end
   end
 
+  shared_examples 'it should have Gmail Actions links' do
+    it { is_expected.to have_body_text /ViewAction/ }
+  end
+
+  shared_examples 'it should not have Gmail Actions links' do
+    it { is_expected.to_not have_body_text /ViewAction/ }
+  end
+
+  shared_examples 'it should show Gmail Actions View Issue link' do
+    it_behaves_like 'it should have Gmail Actions links'
+
+    it { is_expected.to have_body_text /View Issue/ }
+  end
+
+  shared_examples 'it should show Gmail Actions View Merge request link' do
+    it_behaves_like 'it should have Gmail Actions links'
+
+    it { is_expected.to have_body_text /View Merge request/ }
+  end
+
+  shared_examples 'it should show Gmail Actions View Commit link' do
+    it_behaves_like 'it should have Gmail Actions links'
+
+    it { is_expected.to have_body_text /View Commit/ }
+  end
+
   describe 'for new users, the email' do
     let(:example_site_path) { root_path }
     let(:new_user) { create(:user, email: new_user_address, created_by_id: 1) }
@@ -87,6 +113,7 @@ describe Notify do
 
     it_behaves_like 'an email sent from GitLab'
     it_behaves_like 'a new user email', new_user_address
+    it_behaves_like 'it should not have Gmail Actions links'
 
     it 'contains the password text' do
       is_expected.to have_body_text /Click here to set your password/
@@ -115,6 +142,7 @@ describe Notify do
 
     it_behaves_like 'an email sent from GitLab'
     it_behaves_like 'a new user email', new_user_address
+    it_behaves_like 'it should not have Gmail Actions links'
 
     it 'should not contain the new user\'s password' do
       is_expected.not_to have_body_text /password/
@@ -127,6 +155,7 @@ describe Notify do
     subject { Notify.new_ssh_key_email(key.id) }
 
     it_behaves_like 'an email sent from GitLab'
+    it_behaves_like 'it should not have Gmail Actions links'
 
     it 'is sent to the new user' do
       is_expected.to deliver_to key.user.email
@@ -150,6 +179,8 @@ describe Notify do
 
     subject { Notify.new_email_email(email.id) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent to the new user' do
       is_expected.to deliver_to email.user.email
     end
@@ -194,6 +225,7 @@ describe Notify do
 
           it_behaves_like 'an assignee email'
           it_behaves_like 'an email starting a new thread', 'issue'
+          it_behaves_like 'it should show Gmail Actions View Issue link'
 
           it 'has the correct subject' do
             is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
@@ -207,16 +239,19 @@ describe Notify do
         describe 'that are new with a description' do
           subject { Notify.new_issue_email(issue_with_description.assignee_id, issue_with_description.id) }
 
+          it_behaves_like 'it should show Gmail Actions View Issue link'
+
           it 'contains the description' do
             is_expected.to have_body_text /#{issue_with_description.description}/
           end
         end
 
         describe 'that have been reassigned' do
-          subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user) }
+          subject { Notify.reassigned_issue_email(recipient.id, issue.id, previous_assignee.id, current_user.id) }
 
           it_behaves_like 'a multiple recipients email'
           it_behaves_like 'an answer to an existing thread', 'issue'
+          it_behaves_like 'it should show Gmail Actions View Issue link'
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -243,9 +278,10 @@ describe Notify do
 
         describe 'status changed' do
           let(:status) { 'closed' }
-          subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user) }
+          subject { Notify.issue_status_changed_email(recipient.id, issue.id, status, current_user.id) }
 
           it_behaves_like 'an answer to an existing thread', 'issue'
+          it_behaves_like 'it should show Gmail Actions View Issue link'
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -269,7 +305,6 @@ describe Notify do
             is_expected.to have_body_text /#{namespace_project_issue_path project.namespace, project, issue}/
           end
         end
-
       end
 
       context 'for merge requests' do
@@ -282,6 +317,7 @@ describe Notify do
 
           it_behaves_like 'an assignee email'
           it_behaves_like 'an email starting a new thread', 'merge_request'
+          it_behaves_like 'it should show Gmail Actions View Merge request link'
 
           it 'has the correct subject' do
             is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -307,6 +343,8 @@ describe Notify do
         describe 'that are new with a description' do
           subject { Notify.new_merge_request_email(merge_request_with_description.assignee_id, merge_request_with_description.id) }
 
+          it_behaves_like 'it should show Gmail Actions View Merge request link'
+
           it 'contains the description' do
             is_expected.to have_body_text /#{merge_request_with_description.description}/
           end
@@ -317,6 +355,7 @@ describe Notify do
 
           it_behaves_like 'a multiple recipients email'
           it_behaves_like 'an answer to an existing thread', 'merge_request'
+          it_behaves_like 'it should show Gmail Actions View Merge request link'
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -343,9 +382,10 @@ describe Notify do
 
         describe 'status changed' do
           let(:status) { 'reopened' }
-          subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user) }
+          subject { Notify.merge_request_status_email(recipient.id, merge_request.id, status, current_user.id) }
 
           it_behaves_like 'an answer to an existing thread', 'merge_request'
+          it_behaves_like 'it should show Gmail Actions View Merge request link'
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -375,6 +415,7 @@ describe Notify do
 
           it_behaves_like 'a multiple recipients email'
           it_behaves_like 'an answer to an existing thread', 'merge_request'
+          it_behaves_like 'it should show Gmail Actions View Merge request link'
 
           it 'is sent as the merge author' do
             sender = subject.header[:from].addrs[0]
@@ -403,6 +444,7 @@ describe Notify do
       subject { Notify.project_was_moved_email(project.id, user.id, "gitlab/gitlab") }
 
       it_behaves_like 'an email sent from GitLab'
+      it_behaves_like 'it should not have Gmail Actions links'
 
       it 'has the correct subject' do
         is_expected.to have_subject /Project was moved/
@@ -424,13 +466,16 @@ describe Notify do
       subject { Notify.project_access_granted_email(project_member.id) }
 
       it_behaves_like 'an email sent from GitLab'
+      it_behaves_like 'it should not have Gmail Actions links'
 
       it 'has the correct subject' do
         is_expected.to have_subject /Access to project was granted/
       end
+
       it 'contains name of project' do
         is_expected.to have_body_text /#{project.name}/
       end
+
       it 'contains new user role' do
         is_expected.to have_body_text /#{project_member.human_access}/
       end
@@ -445,6 +490,8 @@ describe Notify do
       end
 
       shared_examples 'a note email' do
+        it_behaves_like 'it should have Gmail Actions links'
+
         it 'is sent as the author' do
           sender = subject.header[:from].addrs[0]
           expect(sender.display_name).to eq(note_author.name)
@@ -469,6 +516,7 @@ describe Notify do
 
         it_behaves_like 'a note email'
         it_behaves_like 'an answer to an existing thread', 'commit'
+        it_behaves_like 'it should show Gmail Actions View Commit link'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
@@ -488,6 +536,7 @@ describe Notify do
 
         it_behaves_like 'a note email'
         it_behaves_like 'an answer to an existing thread', 'merge_request'
+        it_behaves_like 'it should show Gmail Actions View Merge request link'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -507,6 +556,7 @@ describe Notify do
 
         it_behaves_like 'a note email'
         it_behaves_like 'an answer to an existing thread', 'issue'
+        it_behaves_like 'it should show Gmail Actions View Issue link'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
@@ -527,6 +577,7 @@ describe Notify do
     subject { Notify.group_access_granted_email(membership.id) }
 
     it_behaves_like 'an email sent from GitLab'
+    it_behaves_like 'it should not have Gmail Actions links'
 
     it 'has the correct subject' do
       is_expected.to have_subject /Access to group was granted/
@@ -546,8 +597,10 @@ describe Notify do
     let(:user) { create(:user, email: 'old-email@mail.com') }
 
     before do
-      user.email = "new-email@mail.com"
-      user.save
+      perform_enqueued_jobs do
+        user.email = "new-email@mail.com"
+        user.save
+      end
     end
 
     subject { ActionMailer::Base.deliveries.last }
@@ -574,6 +627,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
@@ -600,6 +655,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
@@ -625,6 +682,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
@@ -646,6 +705,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
@@ -671,6 +732,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
 
+    it_behaves_like 'it should not have Gmail Actions links'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
@@ -774,6 +837,8 @@ describe Notify do
 
     subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
 
+    it_behaves_like 'it should show Gmail Actions View Commit link'
+
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
       expect(sender.display_name).to eq(user.name)
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index f01fe8bd3982e9c0418f74725a3be569b6edb2b6..dfbac7b4004600c782713ec5f115f385c1617eb3 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -23,6 +23,10 @@
 #  after_sign_out_path          :string(255)
 #  session_expire_delay         :integer          default(10080), not null
 #  import_sources               :text
+#  help_page_text               :text
+#  admin_notification_email     :string(255)
+#  shared_runners_enabled       :boolean          default(TRUE), not null
+#  max_artifacts_size           :integer          default(100), not null
 #
 
 require 'spec_helper'
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index 44dbd083f064cc193c9e056977e0d9dcaf048310..a13f6458cac7bfb960893214986642518247dca4 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -1,18 +1,19 @@
 # == Schema Information
 #
-# Table name: commits
+# Table name: ci_commits
 #
-#  id           :integer          not null, primary key
-#  project_id   :integer
-#  ref          :string(255)
-#  sha          :string(255)
-#  before_sha   :string(255)
-#  push_data    :text
-#  created_at   :datetime
-#  updated_at   :datetime
-#  tag          :boolean          default(FALSE)
-#  yaml_errors  :text
-#  committed_at :datetime
+#  id            :integer          not null, primary key
+#  project_id    :integer
+#  ref           :string(255)
+#  sha           :string(255)
+#  before_sha    :string(255)
+#  push_data     :text
+#  created_at    :datetime
+#  updated_at    :datetime
+#  tag           :boolean          default(FALSE)
+#  yaml_errors   :text
+#  committed_at  :datetime
+#  gl_project_id :integer
 #
 
 require 'spec_helper'
diff --git a/spec/models/ci/project_services/mail_service_spec.rb b/spec/models/ci/project_services/mail_service_spec.rb
index d9b3d34ff1529bd04343f399b4b2fef6aac60a35..c03be3ef75f082e2d4128413de24c4d9bf666087 100644
--- a/spec/models/ci/project_services/mail_service_spec.rb
+++ b/spec/models/ci/project_services/mail_service_spec.rb
@@ -44,13 +44,10 @@ describe Ci::MailService do
       end
 
       it do
-        should_email("git@example.com")
-        mail.execute(build)
-      end
-
-      def should_email(email)
-        expect(Ci::Notify).to receive(:build_fail_email).with(build.id, email)
-        expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
+        perform_enqueued_jobs do
+          expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
+          expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"])
+        end
       end
     end
 
@@ -67,13 +64,10 @@ describe Ci::MailService do
       end
 
       it do
-        should_email("git@example.com")
-        mail.execute(build)
-      end
-
-      def should_email(email)
-        expect(Ci::Notify).to receive(:build_success_email).with(build.id, email)
-        expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+        perform_enqueued_jobs do
+          expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
+          expect(ActionMailer::Base.deliveries.last.to).to eq(["git@example.com"])
+        end
       end
     end
 
@@ -95,14 +89,12 @@ describe Ci::MailService do
       end
 
       it do
-        should_email("git@example.com")
-        should_email("jeroen@example.com")
-        mail.execute(build)
-      end
-
-      def should_email(email)
-        expect(Ci::Notify).to receive(:build_success_email).with(build.id, email)
-        expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+        perform_enqueued_jobs do
+          expect{ mail.execute(build) }.to change{ ActionMailer::Base.deliveries.size }.by(2)
+          expect(
+            ActionMailer::Base.deliveries.map(&:to).flatten
+          ).to include("git@example.com", "jeroen@example.com")
+        end
       end
     end
 
@@ -124,14 +116,11 @@ describe Ci::MailService do
       end
 
       it do
-        should_email(commit.git_author_email)
-        should_email("jeroen@example.com")
-        mail.execute(build) if mail.can_execute?(build)
-      end
-
-      def should_email(email)
-        expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
-        expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+        perform_enqueued_jobs do
+          expect do
+            mail.execute(build) if mail.can_execute?(build)
+          end.to_not change{ ActionMailer::Base.deliveries.size }
+        end
       end
     end
 
@@ -177,14 +166,11 @@ describe Ci::MailService do
 
       it do
         Ci::Build.retry(build)
-        should_email(commit.git_author_email)
-        should_email("jeroen@example.com")
-        mail.execute(build) if mail.can_execute?(build)
-      end
-
-      def should_email(email)
-        expect(Ci::Notify).not_to receive(:build_success_email).with(build.id, email)
-        expect(Ci::Notify).not_to receive(:build_fail_email).with(build.id, email)
+        perform_enqueued_jobs do
+          expect do
+            mail.execute(build) if mail.can_execute?(build)
+          end.to_not change{ ActionMailer::Base.deliveries.size }
+        end
       end
     end
   end
diff --git a/spec/models/ci/project_spec.rb b/spec/models/ci/project_spec.rb
index 490c6a679821b02ddd09c1b3c957cf0ef829ece1..ac7e38bbcb05dbfeea94325e345539e3baa0de20 100644
--- a/spec/models/ci/project_spec.rb
+++ b/spec/models/ci/project_spec.rb
@@ -1,9 +1,9 @@
 # == Schema Information
 #
-# Table name: projects
+# Table name: ci_projects
 #
 #  id                       :integer          not null, primary key
-#  name                     :string(255)      not null
+#  name                     :string(255)
 #  timeout                  :integer          default(3600), not null
 #  created_at               :datetime
 #  updated_at               :datetime
@@ -28,8 +28,8 @@
 require 'spec_helper'
 
 describe Ci::Project do
-  let(:gl_project) { FactoryGirl.create :empty_project }
-  let(:project) { FactoryGirl.create :ci_project, gl_project: gl_project }
+  let(:project) { FactoryGirl.create :ci_project }
+  let(:gl_project) { project.gl_project }
   subject { project }
 
   it { is_expected.to have_many(:runner_projects) }
@@ -194,18 +194,6 @@ describe Ci::Project do
     end
   end
 
-  describe 'Project.parse' do
-    let(:project) { FactoryGirl.create :project }
-
-    subject { Ci::Project.parse(project) }
-
-    it { is_expected.to be_valid }
-    it { is_expected.to be_kind_of(Ci::Project) }
-    it { expect(subject.name).to eq(project.name_with_namespace) }
-    it { expect(subject.gitlab_id).to eq(project.id) }
-    it { expect(subject.gitlab_url).to eq(project.web_url) }
-  end
-
   describe :repo_url_with_auth do
     let(:project) { FactoryGirl.create :ci_project }
     subject { project.repo_url_with_auth }
diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb
index 0218d48413008256d4ac34b9e789134e82be4533..37682c6ea0cc81489e8f6f3e4d4b06b51043ee63 100644
--- a/spec/models/ci/runner_project_spec.rb
+++ b/spec/models/ci/runner_project_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runner_projects
+# Table name: ci_runner_projects
 #
 #  id         :integer          not null, primary key
 #  runner_id  :integer          not null
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index f8a51c29dc2c0216cd3187e2a5ec8514dd16902c..9a1233b909536c6daf629a10ea0f68d48f14c0c3 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: runners
+# Table name: ci_runners
 #
 #  id           :integer          not null, primary key
 #  token        :string(255)
diff --git a/spec/models/ci/service_spec.rb b/spec/models/ci/service_spec.rb
index 2df70e888881a4777fcaff2274bf93f3706cce04..36cda988eb472fa79fc7312ecf77f2430a1e59f2 100644
--- a/spec/models/ci/service_spec.rb
+++ b/spec/models/ci/service_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: services
+# Table name: ci_services
 #
 #  id         :integer          not null, primary key
 #  type       :string(255)
diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb
index 19c14ef2da200cdf2c666d21499d58837c5f7a20..b8aa3c1e7775c468babbfb95cec25d59b85f3b82 100644
--- a/spec/models/ci/trigger_spec.rb
+++ b/spec/models/ci/trigger_spec.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: ci_triggers
+#
+#  id         :integer          not null, primary key
+#  token      :string(255)
+#  project_id :integer          not null
+#  deleted_at :datetime
+#  created_at :datetime
+#  updated_at :datetime
+#
+
 require 'spec_helper'
 
 describe Ci::Trigger do
diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb
index d034a6c7b9f550c13e1f485e4535a5135e1d57e4..a515f5881ff00860f60842eb04555bc3da534318 100644
--- a/spec/models/ci/variable_spec.rb
+++ b/spec/models/ci/variable_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: variables
+# Table name: ci_variables
 #
 #  id                   :integer          not null, primary key
 #  project_id           :integer          not null
diff --git a/spec/models/ci/web_hook_spec.rb b/spec/models/ci/web_hook_spec.rb
index bf9481ab81d26c354eebd397229c95bae8bdf375..2865482a2120b35bc15654b48f3194e812654f57 100644
--- a/spec/models/ci/web_hook_spec.rb
+++ b/spec/models/ci/web_hook_spec.rb
@@ -1,6 +1,6 @@
 # == Schema Information
 #
-# Table name: web_hooks
+# Table name: ci_web_hooks
 #
 #  id         :integer          not null, primary key
 #  url        :string(255)      not null
diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb
index c96a606fdaab60445d7415e85763049530672317..dca0715eed8f5a72419ed6711ad96bef406e55de 100644
--- a/spec/models/commit_status_spec.rb
+++ b/spec/models/commit_status_spec.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 require 'spec_helper'
 
 describe CommitStatus do
diff --git a/spec/models/concerns/strip_attribute_spec.rb b/spec/models/concerns/strip_attribute_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6445e29c3efb3584ad71f1617c9fdfa4940e9587
--- /dev/null
+++ b/spec/models/concerns/strip_attribute_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe Milestone, "StripAttribute" do
+  let(:milestone) { create(:milestone) }
+
+  describe ".strip_attributes" do
+    it { expect(Milestone).to respond_to(:strip_attributes) }
+    it { expect(Milestone.strip_attrs).to include(:title) }
+  end
+
+  describe "#strip_attributes" do
+    before do
+      milestone.title = '    8.3   '
+      milestone.valid?
+    end
+
+    it { expect(milestone.title).to eq('8.3') }
+  end
+
+end
diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb
index 0f32f162a104b70ff3d124b0cf9fcece128b6b94..ae53f7a536b25638795a87bf8cda1ebe48acb19f 100644
--- a/spec/models/event_spec.rb
+++ b/spec/models/event_spec.rb
@@ -64,4 +64,42 @@ describe Event do
     it { expect(@event.branch_name).to eq("master") }
     it { expect(@event.author).to eq(@user) }
   end
+
+  describe '.latest_update_time' do
+    describe 'when events are present' do
+      let(:time) { Time.utc(2015, 1, 1) }
+
+      before do
+        create(:closed_issue_event, updated_at: time)
+        create(:closed_issue_event, updated_at: time + 5)
+      end
+
+      it 'returns the latest update time' do
+        expect(Event.latest_update_time).to eq(time + 5)
+      end
+    end
+
+    describe 'when no events exist' do
+      it 'returns nil' do
+        expect(Event.latest_update_time).to be_nil
+      end
+    end
+  end
+
+  describe '.limit_recent' do
+    let!(:event1) { create(:closed_issue_event) }
+    let!(:event2) { create(:closed_issue_event) }
+
+    describe 'without an explicit limit' do
+      subject { Event.limit_recent }
+
+      it { is_expected.to eq([event2, event1]) }
+    end
+
+    describe 'with an explicit limit' do
+      subject { Event.limit_recent(1) }
+
+      it { is_expected.to eq([event2]) }
+    end
+  end
 end
diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb
index f442fa5fbe5f608b80f68dcddca4d215575e23b3..c86314c454c82f965f272b83f8aafc4cbb9e3e3d 100644
--- a/spec/models/generic_commit_status_spec.rb
+++ b/spec/models/generic_commit_status_spec.rb
@@ -1,3 +1,36 @@
+# == Schema Information
+#
+# Table name: ci_builds
+#
+#  id                 :integer          not null, primary key
+#  project_id         :integer
+#  status             :string(255)
+#  finished_at        :datetime
+#  trace              :text
+#  created_at         :datetime
+#  updated_at         :datetime
+#  started_at         :datetime
+#  runner_id          :integer
+#  coverage           :float
+#  commit_id          :integer
+#  commands           :text
+#  job_id             :integer
+#  name               :string(255)
+#  deploy             :boolean          default(FALSE)
+#  options            :text
+#  allow_failure      :boolean          default(FALSE), not null
+#  stage              :string(255)
+#  trigger_request_id :integer
+#  stage_idx          :integer
+#  tag                :boolean
+#  ref                :string(255)
+#  user_id            :integer
+#  type               :string(255)
+#  target_url         :string(255)
+#  description        :string(255)
+#  artifacts_file     :text
+#
+
 require 'spec_helper'
 
 describe GenericCommitStatus do
diff --git a/spec/models/global_milestone_spec.rb b/spec/models/global_milestone_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6eeff30b20e1ef88195ffed18991855a43ccddb5
--- /dev/null
+++ b/spec/models/global_milestone_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe GlobalMilestone do
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let(:group) { create(:group) }
+  let(:project1) { create(:project, group: group) }
+  let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
+  let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
+  let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
+  let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
+  let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) }
+  let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) }
+  let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
+  let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
+
+  describe :build_collection do
+    before do
+      milestones =
+        [
+          milestone1_project1,
+          milestone1_project2,
+          milestone1_project3,
+          milestone2_project1,
+          milestone2_project2,
+          milestone2_project3
+        ]
+
+      @global_milestones = GlobalMilestone.build_collection(milestones)
+    end
+
+    it 'should have all project milestones' do
+      expect(@global_milestones.count).to eq(2)
+    end
+
+    it 'should have all project milestones titles' do
+      expect(@global_milestones.map(&:title)).to match_array(['Milestone v1.2', 'VD-123'])
+    end
+
+    it 'should have all project milestones' do
+      expect(@global_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6)
+    end
+  end
+
+  describe :initialize do
+    before do
+      milestones =
+        [
+          milestone1_project1,
+          milestone1_project2,
+          milestone1_project3,
+        ]
+
+      @global_milestone = GlobalMilestone.new(milestone1_project1.title, milestones)
+    end
+
+    it 'should have exactly one group milestone' do
+      expect(@global_milestone.title).to eq('Milestone v1.2')
+    end
+
+    it 'should have all project milestones with the same title' do
+      expect(@global_milestone.milestones.count).to eq(3)
+    end
+  end
+end
diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb
index 0f23e81ace9f3f1d8e23f3d6524c989b194ed68f..6f166b5ab7527c60f3442e513085603a8d6a74e0 100644
--- a/spec/models/group_spec.rb
+++ b/spec/models/group_spec.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'spec_helper'
@@ -37,6 +38,33 @@ describe Group do
     it { is_expected.not_to validate_presence_of :owner }
   end
 
+  describe '.public_and_given_groups' do
+    let!(:public_group) { create(:group, public: true) }
+
+    subject { described_class.public_and_given_groups([group.id]) }
+
+    it { is_expected.to eq([public_group, group]) }
+  end
+
+  describe '.visible_to_user' do
+    let!(:group) { create(:group) }
+    let!(:user)  { create(:user) }
+
+    subject { described_class.visible_to_user(user) }
+
+    describe 'when the user has access to a group' do
+      before do
+        group.add_user(user, Gitlab::Access::MASTER)
+      end
+
+      it { is_expected.to eq([group]) }
+    end
+
+    describe 'when the user does not have access to any groups' do
+      it { is_expected.to eq([]) }
+    end
+  end
+
   describe '#to_reference' do
     it 'returns a String reference to the object' do
       expect(group.to_reference).to eq "@#{group.name}"
diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb
index 6518213d71c2581449cb5706bb01cf7073a66e2a..511ee8cbd96521de19c941f32786003706e59964 100644
--- a/spec/models/label_spec.rb
+++ b/spec/models/label_spec.rb
@@ -8,6 +8,7 @@
 #  project_id :integer
 #  created_at :datetime
 #  updated_at :datetime
+#  template   :boolean          default(FALSE)
 #
 
 require 'spec_helper'
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index eed2cbc54128104814664888ca94702bdbb07a73..567c911425c8d526e67c962e8d5f1b59a6d2cb8e 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -20,6 +20,7 @@
 #  position          :integer          default(0)
 #  locked_at         :datetime
 #  updated_by_id     :integer
+#  merge_error       :string(255)
 #
 
 require 'spec_helper'
@@ -192,4 +193,29 @@ describe MergeRequest do
   it_behaves_like 'a Taskable' do
     subject { create :merge_request, :simple }
   end
+
+  describe '#ci_commit' do
+    describe 'when the source project exists' do
+      it 'returns the latest commit' do
+        commit    = double(:commit, id: '123abc')
+        ci_commit = double(:ci_commit)
+
+        allow(subject).to receive(:last_commit).and_return(commit)
+
+        expect(subject.source_project).to receive(:ci_commit).
+          with('123abc').
+          and_return(ci_commit)
+
+        expect(subject.ci_commit).to eq(ci_commit)
+      end
+    end
+
+    describe 'when the source project does not exist' do
+      it 'returns nil' do
+        allow(subject).to receive(:source_project).and_return(nil)
+
+        expect(subject.ci_commit).to be_nil
+      end
+    end
+  end
 end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 1d72a9503ae553b83c448c89c27edb41357ef525..a98b9cb7321205b8791f4fbf47ed298ee4635ffe 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -11,6 +11,7 @@
 #  type        :string(255)
 #  description :string(255)      default(""), not null
 #  avatar      :string(255)
+#  public      :boolean          default(FALSE)
 #
 
 require 'spec_helper'
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 75564839dcfb6baac5c2b44357b1184cd5fc0b73..f347f537550814028dccc8f35e141a3cfb5495f4 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -32,77 +32,6 @@ describe Note do
     it { is_expected.to validate_presence_of(:project) }
   end
 
-  describe '#votable?' do
-    it 'is true for issue notes' do
-      note = build(:note_on_issue)
-      expect(note).to be_votable
-    end
-
-    it 'is true for merge request notes' do
-      note = build(:note_on_merge_request)
-      expect(note).to be_votable
-    end
-
-    it 'is false for merge request diff notes' do
-      note = build(:note_on_merge_request_diff)
-      expect(note).not_to be_votable
-    end
-
-    it 'is false for commit notes' do
-      note = build(:note_on_commit)
-      expect(note).not_to be_votable
-    end
-
-    it 'is false for commit diff notes' do
-      note = build(:note_on_commit_diff)
-      expect(note).not_to be_votable
-    end
-  end
-
-  describe 'voting score' do
-    it 'recognizes a neutral note' do
-      note = build(:votable_note, note: 'This is not a +1 note')
-      expect(note).not_to be_upvote
-      expect(note).not_to be_downvote
-    end
-
-    it 'recognizes a neutral emoji note' do
-      note = build(:votable_note, note: "I would :+1: this, but I don't want to")
-      expect(note).not_to be_upvote
-      expect(note).not_to be_downvote
-    end
-
-    it 'recognizes a +1 note' do
-      note = build(:votable_note, note: '+1 for this')
-      expect(note).to be_upvote
-    end
-
-    it 'recognizes a +1 emoji as a vote' do
-      note = build(:votable_note, note: ':+1: for this')
-      expect(note).to be_upvote
-    end
-
-    it 'recognizes a thumbsup emoji as a vote' do
-      note = build(:votable_note, note: ':thumbsup: for this')
-      expect(note).to be_upvote
-    end
-
-    it 'recognizes a -1 note' do
-      note = build(:votable_note, note: '-1 for this')
-      expect(note).to be_downvote
-    end
-
-    it 'recognizes a -1 emoji as a vote' do
-      note = build(:votable_note, note: ':-1: for this')
-      expect(note).to be_downvote
-    end
-
-    it 'recognizes a thumbsdown emoji as a vote' do
-      note = build(:votable_note, note: ':thumbsdown: for this')
-      expect(note).to be_downvote
-    end
-  end
-
   describe "Commit notes" do
     let!(:note) { create(:note_on_commit, note: "+1 from me") }
     let!(:commit) { note.noteable }
@@ -139,10 +68,6 @@ describe Note do
     it "should be recognized by #for_commit_diff_line?" do
       expect(note).to be_for_commit_diff_line
     end
-
-    it "should not be votable" do
-      expect(note).not_to be_votable
-    end
   end
 
   describe 'authorization' do
@@ -204,4 +129,16 @@ describe Note do
 
     it { expect(Note.search('wow')).to include(note) }
   end
+
+  describe :grouped_awards do
+    before do
+      create :note, note: "smile", is_award: true
+      create :note, note: "smile", is_award: true
+    end
+
+    it "returns grouped array of notes" do
+      expect(Note.grouped_awards.first.first).to eq("smile")
+      expect(Note.grouped_awards.first.last).to match_array(Note.all)
+    end
+  end
 end
diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb
index ddd2cce212c5750bd3e35143a40c179cddb8ab34..576f5fc79eb4660ad5f256bcf49d0cc94f3e35cb 100644
--- a/spec/models/project_services/jira_service_spec.rb
+++ b/spec/models/project_services/jira_service_spec.rb
@@ -94,9 +94,9 @@ describe JiraService do
       end
 
       it 'should be prepopulated with the settings' do
-        expect(@service.properties[:project_url]).to eq('http://jira.sample/projects/project_a')
-        expect(@service.properties[:issues_url]).to eq("http://jira.sample/issues/:id")
-        expect(@service.properties[:new_issue_url]).to eq("http://jira.sample/projects/project_a/issues/new")
+        expect(@service.properties["project_url"]).to eq('http://jira.sample/projects/project_a')
+        expect(@service.properties["issues_url"]).to eq("http://jira.sample/issues/:id")
+        expect(@service.properties["new_issue_url"]).to eq("http://jira.sample/projects/project_a/issues/new")
       end
     end
   end
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f93935ebe3b057e8e69be5c6b8bf12f4382f34a3..06a02c13bf12c048495cc3e802daecfdea10d9ca 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -345,17 +345,6 @@ describe Project do
       expect(project1.star_count).to eq(0)
       expect(project2.star_count).to eq(0)
     end
-
-    it 'is decremented when an upvoter account is deleted' do
-      user = create :user
-      project = create :project, :public
-      user.toggle_star(project)
-      project.reload
-      expect(project.star_count).to eq(1)
-      user.destroy
-      project.reload
-      expect(project.star_count).to eq(0)
-    end
   end
 
   describe :avatar_type do
@@ -415,12 +404,15 @@ describe Project do
     it { expect(project.ci_commit(commit.sha)).to eq(commit) }
   end
 
-  describe :enable_ci do
+  describe :builds_enabled do
     let(:project) { create :project }
 
-    before { project.enable_ci }
+    before { project.builds_enabled = true }
 
-    it { expect(project.gitlab_ci?).to be_truthy }
+    subject { project.builds_enabled }
+
+    it { is_expected.to eq(project.gitlab_ci_service.active) }
+    it { expect(project.builds_enabled?).to be_truthy }
     it { expect(project.gitlab_ci_project).to be_a(Ci::Project) }
   end
 
@@ -452,7 +444,9 @@ describe Project do
 
       before do
         2.times do
-          create(:note_on_commit, project: project2, created_at: date)
+          # Little fix for special issue related to Fractional Seconds support for MySQL.
+          # See: https://github.com/rails/rails/pull/14359/files
+          create(:note_on_commit, project: project2, created_at: date + 1)
         end
       end
 
@@ -461,4 +455,23 @@ describe Project do
       end
     end
   end
+
+  describe '.visible_to_user' do
+    let!(:project) { create(:project, :private) }
+    let!(:user)    { create(:user) }
+
+    subject { described_class.visible_to_user(user) }
+
+    describe 'when a user has access to a project' do
+      before do
+        project.team.add_user(user, Gitlab::Access::MASTER)
+      end
+
+      it { is_expected.to eq([project]) }
+    end
+
+    describe 'when a user does not have access to any projects' do
+      it { is_expected.to eq([]) }
+    end
+  end
 end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 9f6cdeeaa962b1e0b8245d043628f4a65f085b18..3b8891444476f7a48c7b06337eacaa426da29b41 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -184,6 +184,12 @@ describe ProjectWiki do
       subject.create_page("test page", "some content", :markdown, "commit message")
       expect(subject.pages.first.page.version.message).to eq("commit message")
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.create_page('Test Page', 'This is content')
+    end
   end
 
   describe "#update_page" do
@@ -205,6 +211,12 @@ describe ProjectWiki do
     it "sets the correct commit message" do
       expect(@page.version.message).to eq("updated page")
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.update_page(@gollum_page, 'Yet more content', :markdown, 'Updated page again')
+    end
   end
 
   describe "#delete_page" do
@@ -217,6 +229,12 @@ describe ProjectWiki do
       subject.delete_page(@page)
       expect(subject.pages.count).to eq(0)
     end
+
+    it 'updates project activity' do
+      expect(subject).to receive(:update_project_activity)
+
+      subject.delete_page(@page)
+    end
   end
 
   private
diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb
index 527005b2b698adae9c53e09ae84bcd81b6ebc18a..72ecb442a361a7576704efff942c01c2d4250542 100644
--- a/spec/models/release_spec.rb
+++ b/spec/models/release_spec.rb
@@ -1,3 +1,15 @@
+# == Schema Information
+#
+# Table name: releases
+#
+#  id          :integer          not null, primary key
+#  tag         :string(255)
+#  description :text
+#  project_id  :integer
+#  created_at  :datetime
+#  updated_at  :datetime
+#
+
 require 'rails_helper'
 
 RSpec.describe Release, type: :model do
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 49e0bfdd2ec91c8be9ab5f47b2e703e8406ebe0a..4631b12faf1763f79547e1c9daf5e34e32466d0d 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -54,6 +54,8 @@
 #  public_email               :string(255)      default(""), not null
 #  dashboard                  :integer          default(0)
 #  project_view               :integer          default(0)
+#  consumed_timestep          :integer
+#  layout                     :integer          default(0)
 #
 
 require 'spec_helper'
@@ -684,7 +686,7 @@ describe User do
     end
   end
 
-  describe "#contributed_projects_ids" do
+  describe "#contributed_projects" do
     subject { create(:user) }
     let!(:project1) { create(:project) }
     let!(:project2) { create(:project, forked_from_project: project3) }
@@ -699,15 +701,15 @@ describe User do
     end
 
     it "includes IDs for projects the user has pushed to" do
-      expect(subject.contributed_projects_ids).to include(project1.id)
+      expect(subject.contributed_projects).to include(project1)
     end
 
     it "includes IDs for projects the user has had merge requests merged into" do
-      expect(subject.contributed_projects_ids).to include(project3.id)
+      expect(subject.contributed_projects).to include(project3)
     end
 
     it "doesn't include IDs for unrelated projects" do
-      expect(subject.contributed_projects_ids).not_to include(project2.id)
+      expect(subject.contributed_projects).not_to include(project2)
     end
   end
 
@@ -756,4 +758,30 @@ describe User do
       expect(subject.recent_push).to eq(nil)
     end
   end
+
+  describe '#authorized_groups' do
+    let!(:user) { create(:user) }
+    let!(:private_group) { create(:group) }
+
+    before do
+      private_group.add_user(user, Gitlab::Access::MASTER)
+    end
+
+    subject { user.authorized_groups }
+
+    it { is_expected.to eq([private_group]) }
+  end
+
+  describe '#authorized_projects' do
+    let!(:user) { create(:user) }
+    let!(:private_project) { create(:project, :private) }
+
+    before do
+      private_project.team << [user, Gitlab::Access::MASTER]
+    end
+
+    subject { user.authorized_projects }
+
+    it { is_expected.to eq([private_project]) }
+  end
 end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index e9de9e0826dc109fd71b71a196a5c08e009e8ef8..9fc294118ae12e35fee731274c5c4dbcf16cbefa 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -88,8 +88,11 @@ describe API::API, api: true  do
         end
 
         it 'returns projects in the correct order when ci_enabled_first parameter is passed' do
-          [project, project2, project3].each{ |project| project.build_missing_services }
-          project2.gitlab_ci_service.update(active: true)
+          [project, project2, project3].each do |project|
+            project.builds_enabled = false
+            project.build_missing_services
+          end
+          project2.builds_enabled = true
           get api('/projects', user), { ci_enabled_first: 'true' }
           expect(response.status).to eq(200)
           expect(json_response).to be_an Array
diff --git a/spec/requests/api/repositories_spec.rb b/spec/requests/api/repositories_spec.rb
index faf6b77a4629d20ea8dcb1bf84588f35bf4e8af2..4911cdd9da666392af1b2fa2f29b32d27dc2f1c1 100644
--- a/spec/requests/api/repositories_spec.rb
+++ b/spec/requests/api/repositories_spec.rb
@@ -11,81 +11,6 @@ describe API::API, api: true  do
   let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
   let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
 
-  describe "GET /projects/:id/repository/tags" do
-    it "should return an array of project tags" do
-      get api("/projects/#{project.id}/repository/tags", user)
-      expect(response.status).to eq(200)
-      expect(json_response).to be_an Array
-      expect(json_response.first['name']).to eq(project.repo.tags.sort_by(&:name).reverse.first.name)
-    end
-  end
-
-  describe 'POST /projects/:id/repository/tags' do
-    context 'lightweight tags' do
-      it 'should create a new tag' do
-        post api("/projects/#{project.id}/repository/tags", user),
-             tag_name: 'v7.0.1',
-             ref: 'master'
-
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq('v7.0.1')
-      end
-    end
-
-    context 'annotated tag' do
-      it 'should create a new annotated tag' do
-        # Identity must be set in .gitconfig to create annotated tag.
-        repo_path = project.repository.path_to_repo
-        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
-        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
-
-        post api("/projects/#{project.id}/repository/tags", user),
-             tag_name: 'v7.1.0',
-             ref: 'master',
-             message: 'Release 7.1.0'
-
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq('v7.1.0')
-        expect(json_response['message']).to eq('Release 7.1.0')
-      end
-    end
-
-    it 'should deny for user without push access' do
-      post api("/projects/#{project.id}/repository/tags", user2),
-           tag_name: 'v1.9.0',
-           ref: '621491c677087aa243f165eab467bfdfbee00be1'
-      expect(response.status).to eq(403)
-    end
-
-    it 'should return 400 if tag name is invalid' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v 1.0.0',
-           ref: 'master'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Tag name invalid')
-    end
-
-    it 'should return 400 if tag already exists' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v8.0.0',
-           ref: 'master'
-      expect(response.status).to eq(201)
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'v8.0.0',
-           ref: 'master'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Tag already exists')
-    end
-
-    it 'should return 400 if ref name is invalid' do
-      post api("/projects/#{project.id}/repository/tags", user),
-           tag_name: 'mytag',
-           ref: 'foo'
-      expect(response.status).to eq(400)
-      expect(json_response['message']).to eq('Invalid reference name')
-    end
-  end
-
   describe "GET /projects/:id/repository/tree" do
     context "authorized user" do
       before { project.team << [user2, :reporter] }
diff --git a/spec/requests/api/services_spec.rb b/spec/requests/api/services_spec.rb
index c0226605a2320aebc5f85bea2daaeb5e6ba8ba86..b180d2fec77b8fd52197589b5e392c90bd1bf6ba 100644
--- a/spec/requests/api/services_spec.rb
+++ b/spec/requests/api/services_spec.rb
@@ -46,6 +46,7 @@ describe API::API, api: true  do
         delete api("/projects/#{project.id}/services/#{dashed_service}", user)
 
         expect(response.status).to eq(200)
+        project.send(service_method).reload
         expect(project.send(service_method).activated?).to be_falsey
       end
     end
diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..17f2643fd45f25ad1f7076ba363f1ae76699dbd1
--- /dev/null
+++ b/spec/requests/api/tags_spec.rb
@@ -0,0 +1,196 @@
+require 'spec_helper'
+require 'mime/types'
+
+describe API::API, api: true  do
+  include ApiHelpers
+  include RepoHelpers
+
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
+  let!(:guest) { create(:project_member, user: user2, project: project, access_level: ProjectMember::GUEST) }
+
+  describe "GET /projects/:id/repository/tags" do
+    let(:tag_name) { project.repository.tag_names.sort.reverse.first }
+    let(:description) { 'Awesome release!' }
+
+    context 'without releases' do
+      it "should return an array of project tags" do
+        get api("/projects/#{project.id}/repository/tags", user)
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+        expect(json_response.first['name']).to eq(tag_name)
+      end
+    end
+
+    context 'with releases' do
+      before do
+        release = project.releases.find_or_initialize_by(tag: tag_name)
+        release.update_attributes(description: description)
+      end
+
+      it "should return an array of project tags with release info" do
+        get api("/projects/#{project.id}/repository/tags", user)
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+        expect(json_response.first['name']).to eq(tag_name)
+        expect(json_response.first['release']['description']).to eq(description)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/repository/tags' do
+    context 'lightweight tags' do
+      it 'should create a new tag' do
+        post api("/projects/#{project.id}/repository/tags", user),
+             tag_name: 'v7.0.1',
+             ref: 'master'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.0.1')
+      end
+    end
+
+    context 'lightweight tags with release notes' do
+      it 'should create a new tag' do
+        post api("/projects/#{project.id}/repository/tags", user),
+             tag_name: 'v7.0.1',
+             ref: 'master',
+             release_description: 'Wow'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.0.1')
+        expect(json_response['release']['description']).to eq('Wow')
+      end
+    end
+
+    context 'annotated tag' do
+      it 'should create a new annotated tag' do
+        # Identity must be set in .gitconfig to create annotated tag.
+        repo_path = project.repository.path_to_repo
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.name #{user.name}))
+        system(*%W(#{Gitlab.config.git.bin_path} --git-dir=#{repo_path} config user.email #{user.email}))
+
+        post api("/projects/#{project.id}/repository/tags", user),
+             tag_name: 'v7.1.0',
+             ref: 'master',
+             message: 'Release 7.1.0'
+
+        expect(response.status).to eq(201)
+        expect(json_response['name']).to eq('v7.1.0')
+        expect(json_response['message']).to eq('Release 7.1.0')
+      end
+    end
+
+    it 'should deny for user without push access' do
+      post api("/projects/#{project.id}/repository/tags", user2),
+           tag_name: 'v1.9.0',
+           ref: '621491c677087aa243f165eab467bfdfbee00be1'
+      expect(response.status).to eq(403)
+    end
+
+    it 'should return 400 if tag name is invalid' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v 1.0.0',
+           ref: 'master'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Tag name invalid')
+    end
+
+    it 'should return 400 if tag already exists' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v8.0.0',
+           ref: 'master'
+      expect(response.status).to eq(201)
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'v8.0.0',
+           ref: 'master'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Tag already exists')
+    end
+
+    it 'should return 400 if ref name is invalid' do
+      post api("/projects/#{project.id}/repository/tags", user),
+           tag_name: 'mytag',
+           ref: 'foo'
+      expect(response.status).to eq(400)
+      expect(json_response['message']).to eq('Invalid reference name')
+    end
+  end
+
+  describe 'POST /projects/:id/repository/tags/:tag_name/release' do
+    let(:tag_name) { project.repository.tag_names.first }
+    let(:description) { 'Awesome release!' }
+
+    it 'should create description for existing git tag' do
+      post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
+        description: description
+
+      expect(response.status).to eq(201)
+      expect(json_response['tag_name']).to eq(tag_name)
+      expect(json_response['description']).to eq(description)
+    end
+
+    it 'should return 404 if the tag does not exist' do
+      post api("/projects/#{project.id}/repository/tags/foobar/release", user),
+        description: description
+
+      expect(response.status).to eq(404)
+      expect(json_response['message']).to eq('Tag does not exist')
+    end
+
+    context 'on tag with existing release' do
+      before do
+        release = project.releases.find_or_initialize_by(tag: tag_name)
+        release.update_attributes(description: description)
+      end
+
+      it 'should return 409 if there is already a release' do
+        post api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
+          description: description
+
+        expect(response.status).to eq(409)
+        expect(json_response['message']).to eq('Release already exists')
+      end
+    end
+  end
+
+  describe 'PUT id/repository/tags/:tag_name/release' do
+    let(:tag_name) { project.repository.tag_names.first }
+    let(:description) { 'Awesome release!' }
+    let(:new_description) { 'The best release!' }
+
+    context 'on tag with existing release' do
+      before do
+        release = project.releases.find_or_initialize_by(tag: tag_name)
+        release.update_attributes(description: description)
+      end
+
+      it 'should update the release description' do
+        put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
+          description: new_description
+
+        expect(response.status).to eq(200)
+        expect(json_response['tag_name']).to eq(tag_name)
+        expect(json_response['description']).to eq(new_description)
+      end
+    end
+
+    it 'should return 404 if the tag does not exist' do
+      put api("/projects/#{project.id}/repository/tags/foobar/release", user),
+        description: new_description
+
+      expect(response.status).to eq(404)
+      expect(json_response['message']).to eq('Tag does not exist')
+    end
+
+    it 'should return 404 if the release does not exist' do
+      put api("/projects/#{project.id}/repository/tags/#{tag_name}/release", user),
+        description: new_description
+
+      expect(response.status).to eq(404)
+      expect(json_response['message']).to eq('Release does not exist')
+    end
+  end
+end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index 7886a6feca2480b4fd768af892384f4da0ee4a08..c2be045099dc2614bb1f8afb4d7166816a082c12 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -5,7 +5,7 @@ describe Ci::API::API do
 
   let(:runner) { FactoryGirl.create(:ci_runner, tag_list: ["mysql", "ruby"]) }
   let(:project) { FactoryGirl.create(:ci_project) }
-  let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+  let(:gl_project) { project.gl_project }
 
   before do
     stub_ci_commit_to_return_yaml_file
@@ -14,7 +14,7 @@ describe Ci::API::API do
   describe "Builds API for runners" do
     let(:shared_runner) { FactoryGirl.create(:ci_runner, token: "SharedRunner") }
     let(:shared_project) { FactoryGirl.create(:ci_project, name: "SharedProject") }
-    let(:shared_gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: shared_project) }
+    let(:shared_gl_project) { shared_project.gl_project }
 
     before do
       FactoryGirl.create :ci_runner_project, project_id: project.id, runner_id: runner.id
@@ -160,15 +160,13 @@ describe Ci::API::API do
           end
 
           it "using token as parameter" do
-            settings = Gitlab::CurrentSettings::current_application_settings
-            settings.update_attributes(max_artifacts_size: 0)
+            stub_application_setting(max_artifacts_size: 0)
             post authorize_url, { token: build.project.token, filesize: 100 }, headers
             expect(response.status).to eq(413)
           end
 
           it "using token as header" do
-            settings = Gitlab::CurrentSettings::current_application_settings
-            settings.update_attributes(max_artifacts_size: 0)
+            stub_application_setting(max_artifacts_size: 0)
             post authorize_url, { filesize: 100 }, headers_with_token
             expect(response.status).to eq(413)
           end
@@ -220,8 +218,7 @@ describe Ci::API::API do
             end
 
             it do
-              settings = Gitlab::CurrentSettings::current_application_settings
-              settings.update_attributes(max_artifacts_size: 0)
+              stub_application_setting(max_artifacts_size: 0)
               upload_artifacts(file_upload, headers_with_token)
               expect(response.status).to eq(413)
             end
diff --git a/spec/requests/ci/api/commits_spec.rb b/spec/requests/ci/api/commits_spec.rb
index 6049135fd104f799c8757e81824dfc185a7c5495..aa51ba95bcadc1a545bbe9c9a12d5f271a64af1c 100644
--- a/spec/requests/ci/api/commits_spec.rb
+++ b/spec/requests/ci/api/commits_spec.rb
@@ -4,7 +4,7 @@ describe Ci::API::API, 'Commits' do
   include ApiHelpers
 
   let(:project) { FactoryGirl.create(:ci_project) }
-  let(:gl_project) { FactoryGirl.create(:empty_project, gitlab_ci_project: project) }
+  let(:gl_project) { project.gl_project }
   let(:commit) { FactoryGirl.create(:ci_commit, gl_project: gl_project) }
 
   let(:options) do
diff --git a/spec/requests/ci/api/projects_spec.rb b/spec/requests/ci/api/projects_spec.rb
index 53f7f91cc1f7c0a89583ceb294ae794f721c03ff..893fd168d3e9c9cf90b40d633fdd714c3221a6be 100644
--- a/spec/requests/ci/api/projects_spec.rb
+++ b/spec/requests/ci/api/projects_spec.rb
@@ -41,8 +41,8 @@ describe Ci::API::API do
     describe "GET /projects/owned" do
       let!(:gl_project1) {FactoryGirl.create(:empty_project, namespace: user.namespace)}
       let!(:gl_project2) {FactoryGirl.create(:empty_project, namespace: user.namespace)}
-      let!(:project1) { FactoryGirl.create(:ci_project, gl_project: gl_project1) }
-      let!(:project2) { FactoryGirl.create(:ci_project, gl_project: gl_project2) }
+      let!(:project1) { gl_project1.ensure_gitlab_ci_project }
+      let!(:project2) { gl_project2.ensure_gitlab_ci_project }
 
       before do
         project1.gl_project.team << [user, :developer]
@@ -180,87 +180,53 @@ describe Ci::API::API do
     end
   end
 
-  describe "POST /projects" do
-    let(:gl_project) { FactoryGirl.create :empty_project }
-    let(:project_info) do
-      {
-        gitlab_id: gl_project.id
-      }
-    end
-
-    let(:invalid_project_info) { {} }
+  describe "POST /projects/:id/runners/:id" do
+    let(:project) { FactoryGirl.create(:ci_project) }
+    let(:runner) { FactoryGirl.create(:ci_runner) }
 
-    context "with valid project info" do
-      before do
-        options.merge!(project_info)
-      end
+    it "should add the project to the runner" do
+      project.gl_project.team << [user, :master]
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(201)
 
-      it "should create a project with valid data" do
-        post ci_api("/projects"), options
-        expect(response.status).to eq(201)
-        expect(json_response['name']).to eq(gl_project.name_with_namespace)
-      end
+      project.reload
+      expect(project.runners.first.id).to eq(runner.id)
     end
 
-    context "with invalid project info" do
-      before do
-        options.merge!(invalid_project_info)
-      end
+    it "should fail if it tries to link a non-existing project or runner" do
+      post ci_api("/projects/#{project.id}/runners/non-existing"), options
+      expect(response.status).to eq(404)
 
-      it "should error with invalid data" do
-        post ci_api("/projects"), options
-        expect(response.status).to eq(400)
-      end
+      post ci_api("/projects/non-existing/runners/#{runner.id}"), options
+      expect(response.status).to eq(404)
     end
 
-    describe "POST /projects/:id/runners/:id" do
-      let(:project) { FactoryGirl.create(:ci_project) }
-      let(:runner) { FactoryGirl.create(:ci_runner) }
-
-      it "should add the project to the runner" do
-        project.gl_project.team << [user, :master]
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(201)
-
-        project.reload
-        expect(project.runners.first.id).to eq(runner.id)
-      end
-
-      it "should fail if it tries to link a non-existing project or runner" do
-        post ci_api("/projects/#{project.id}/runners/non-existing"), options
-        expect(response.status).to eq(404)
-
-        post ci_api("/projects/non-existing/runners/#{runner.id}"), options
-        expect(response.status).to eq(404)
-      end
-
-      it "non-manager is not authorized" do
-        allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false)
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(401)
-      end
+    it "non-manager is not authorized" do
+      allow_any_instance_of(User).to receive(:can_manage_project?).and_return(false)
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(401)
     end
+  end
 
-    describe "DELETE /projects/:id/runners/:id" do
-      let(:project) { FactoryGirl.create(:ci_project) }
-      let(:runner) { FactoryGirl.create(:ci_runner) }
+  describe "DELETE /projects/:id/runners/:id" do
+    let(:project) { FactoryGirl.create(:ci_project) }
+    let(:runner) { FactoryGirl.create(:ci_runner) }
 
-      it "should remove the project from the runner" do
-        project.gl_project.team << [user, :master]
-        post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+    it "should remove the project from the runner" do
+      project.gl_project.team << [user, :master]
+      post ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
 
-        expect(project.runners).to be_present
-        delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(200)
+      expect(project.runners).to be_present
+      delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(200)
 
-        project.reload
-        expect(project.runners).to be_empty
-      end
+      project.reload
+      expect(project.runners).to be_empty
+    end
 
-      it "non-manager is not authorized" do
-        delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
-        expect(response.status).to eq(401)
-      end
+    it "non-manager is not authorized" do
+      delete ci_api("/projects/#{project.id}/runners/#{runner.id}"), options
+      expect(response.status).to eq(401)
     end
   end
 end
diff --git a/spec/requests/ci/api/triggers_spec.rb b/spec/requests/ci/api/triggers_spec.rb
index 93617fc4b3f2dc9eee0b9656a3d53f1aab77b7e3..a2b436d58119afc69f80b8affc9bc0f1c4c85a9e 100644
--- a/spec/requests/ci/api/triggers_spec.rb
+++ b/spec/requests/ci/api/triggers_spec.rb
@@ -6,7 +6,7 @@ describe Ci::API::API do
   describe 'POST /projects/:project_id/refs/:ref/trigger' do
     let!(:trigger_token) { 'secure token' }
     let!(:gl_project) { FactoryGirl.create(:project) }
-    let!(:project) { FactoryGirl.create(:ci_project, gl_project: gl_project) }
+    let!(:project) { gl_project.ensure_gitlab_ci_project }
     let!(:project2) { FactoryGirl.create(:ci_project) }
     let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
     let(:options) do
diff --git a/spec/services/ci/create_commit_service_spec.rb b/spec/services/ci/create_commit_service_spec.rb
index e3a8fe9681b6c87a84c1ff7732deae4d8f9be062..e0ede1d58b71a300b8902afcadfe39e59c892ae1 100644
--- a/spec/services/ci/create_commit_service_spec.rb
+++ b/spec/services/ci/create_commit_service_spec.rb
@@ -53,7 +53,7 @@ module Ci
         end
       end
 
-      it 'fails commits without .gitlab-ci.yml' do
+      it 'skips commits without .gitlab-ci.yml' do
         stub_ci_commit_yaml_file(nil)
         result = service.execute(project, user,
                                  ref: 'refs/heads/0_1',
@@ -63,7 +63,24 @@ module Ci
         )
         expect(result).to be_persisted
         expect(result.builds.any?).to be_falsey
-        expect(result.status).to eq('failed')
+        expect(result.status).to eq('skipped')
+        expect(result.yaml_errors).to be_nil
+      end
+
+      it 'skips commits if yaml is invalid' do
+        message = 'message'
+        allow_any_instance_of(Ci::Commit).to receive(:git_commit_message) { message }
+        stub_ci_commit_yaml_file('invalid: file: file')
+        commits = [{ message: message }]
+        commit = service.execute(project, user,
+                                 ref: 'refs/tags/0_1',
+                                 before: '00000000',
+                                 after: '31das312',
+                                 commits: commits
+        )
+        expect(commit.builds.any?).to be false
+        expect(commit.status).to eq('failed')
+        expect(commit.yaml_errors).to_not be_nil
       end
 
       describe :ci_skip? do
@@ -100,7 +117,7 @@ module Ci
         end
 
         it "skips builds creation if there is [ci skip] tag in commit message and yaml is invalid" do
-          stub_ci_commit_yaml_file('invalid: file')
+          stub_ci_commit_yaml_file('invalid: file: fiile')
           commits = [{ message: message }]
           commit = service.execute(project, user,
                                    ref: 'refs/tags/0_1',
@@ -110,6 +127,7 @@ module Ci
           )
           expect(commit.builds.any?).to be false
           expect(commit.status).to eq("skipped")
+          expect(commit.yaml_errors).to be_nil
         end
       end
 
diff --git a/spec/services/ci/create_trigger_request_service_spec.rb b/spec/services/ci/create_trigger_request_service_spec.rb
index fcafae386448090f89f881b6b5dd5d7508847855..2ef4bb50a57d0edb5745b4d37939961fa050c27c 100644
--- a/spec/services/ci/create_trigger_request_service_spec.rb
+++ b/spec/services/ci/create_trigger_request_service_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe Ci::CreateTriggerRequestService do
   let(:service) { Ci::CreateTriggerRequestService.new }
   let(:gl_project) { create(:project) }
-  let(:project) { create(:ci_project, gl_project: gl_project) }
+  let(:project) { gl_project.ensure_gitlab_ci_project }
   let(:trigger) { create(:ci_trigger, project: project) }
 
   before do
diff --git a/spec/services/create_release_service_spec.rb b/spec/services/create_release_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..26d7f365bbb6836d95fb7c7a1a61c10072d4f394
--- /dev/null
+++ b/spec/services/create_release_service_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe CreateReleaseService do
+  let(:project) { create(:project) }
+  let(:user) { create(:user) }
+  let(:tag_name) { project.repository.tag_names.first }
+  let(:description) { 'Awesome release!' }
+  let(:service) { CreateReleaseService.new(project, user) }
+
+  it 'creates a new release' do
+    result = service.execute(tag_name, description)
+    expect(result[:status]).to eq(:success)
+    release = project.releases.find_by(tag: tag_name)
+    expect(release).not_to be_nil
+    expect(release.description).to eq(description)
+  end
+
+  it 'raises an error if the tag does not exist' do
+    result = service.execute("foobar", description)
+    expect(result[:status]).to eq(:error)
+  end
+
+  context 'there already exists a release on a tag' do
+    before do
+      service.execute(tag_name, description)
+    end
+
+    it 'raises an error and does not update the release' do
+      result = service.execute(tag_name, 'The best release!')
+      expect(result[:status]).to eq(:error)
+      expect(project.releases.find_by(tag: tag_name).description).to eq(description)
+    end
+  end
+end
diff --git a/spec/services/issues/close_service_spec.rb b/spec/services/issues/close_service_spec.rb
index db547ce0d50a696f1c32b40f88c4da462d1bbd53..d711e3da104f2a5f4a63f0b48927675c18fbad0d 100644
--- a/spec/services/issues/close_service_spec.rb
+++ b/spec/services/issues/close_service_spec.rb
@@ -14,7 +14,9 @@ describe Issues::CloseService do
   describe :execute do
     context "valid params" do
       before do
-        @issue = Issues::CloseService.new(project, user, {}).execute(issue)
+        perform_enqueued_jobs do
+          @issue = Issues::CloseService.new(project, user, {}).execute(issue)
+        end
       end
 
       it { expect(@issue).to be_valid }
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index a91be3b44721541e30c760509925945bbc3c5803..73d0b7f7abe5f291f9c0fc1fc4247bc58ee91bd1 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -3,16 +3,29 @@ require 'spec_helper'
 describe Issues::UpdateService do
   let(:user) { create(:user) }
   let(:user2) { create(:user) }
-  let(:issue) { create(:issue, title: 'Old title') }
+  let(:user3) { create(:user) }
+  let(:issue) { create(:issue, title: 'Old title', assignee_id: user3.id) }
   let(:label) { create(:label) }
   let(:project) { issue.project }
 
   before do
     project.team << [user, :master]
     project.team << [user2, :developer]
+    project.team << [user3, :developer]
   end
 
   describe 'execute' do
+    def find_note(starting_with)
+      @issue.notes.find do |note|
+        note && note.note.start_with?(starting_with)
+      end
+    end
+
+    def update_issue(opts)
+      @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+      @issue.reload
+    end
+
     context "valid params" do
       before do
         opts = {
@@ -23,7 +36,10 @@ describe Issues::UpdateService do
           label_ids: [label.id]
         }
 
-        @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+        perform_enqueued_jobs do
+          @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+        end
+
         @issue.reload
       end
 
@@ -34,18 +50,14 @@ describe Issues::UpdateService do
       it { expect(@issue.labels.count).to eq(1) }
       it { expect(@issue.labels.first.title).to eq('Bug') }
 
-      it 'should send email to user2 about assign of new issue' do
-        email = ActionMailer::Base.deliveries.last
-        expect(email.to.first).to eq(user2.email)
+      it 'should send email to user2 about assign of new issue and email to user3 about issue unassignment' do
+        deliveries = ActionMailer::Base.deliveries
+        email = deliveries.last
+        recipients = deliveries.last(2).map(&:to).flatten
+        expect(recipients).to include(user2.email, user3.email)
         expect(email.subject).to include(issue.title)
       end
 
-      def find_note(starting_with)
-        @issue.notes.find do |note|
-          note && note.note.start_with?(starting_with)
-        end
-      end
-
       it 'should create system note about issue reassign' do
         note = find_note('Reassigned to')
 
@@ -67,5 +79,71 @@ describe Issues::UpdateService do
         expect(note.note).to eq 'Title changed from **Old title** to **New title**'
       end
     end
+
+    context 'when Issue has tasks' do
+      before { update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" }) }
+
+      it { expect(@issue.tasks?).to eq(true) }
+
+      context 'when tasks are marked as completed' do
+        before { update_issue({ description: "- [x] Task 1\n- [X] Task 2" }) }
+
+        it 'creates system note about task status change' do
+          note1 = find_note('Marked the task **Task 1** as completed')
+          note2 = find_note('Marked the task **Task 2** as completed')
+
+          expect(note1).not_to be_nil
+          expect(note2).not_to be_nil
+        end
+      end
+
+      context 'when tasks are marked as incomplete' do
+        before do
+          update_issue({ description: "- [x] Task 1\n- [X] Task 2" })
+          update_issue({ description: "- [ ] Task 1\n- [ ] Task 2" })
+        end
+
+        it 'creates system note about task status change' do
+          note1 = find_note('Marked the task **Task 1** as incomplete')
+          note2 = find_note('Marked the task **Task 2** as incomplete')
+
+          expect(note1).not_to be_nil
+          expect(note2).not_to be_nil
+        end
+      end
+
+      context 'when tasks position has been modified' do
+        before do
+          update_issue({ description: "- [x] Task 1\n- [X] Task 2" })
+          update_issue({ description: "- [x] Task 1\n- [ ] Task 3\n- [ ] Task 2" })
+        end
+
+        it 'does not create a system note' do
+          note = find_note('Marked the task **Task 2** as incomplete')
+
+          expect(note).to be_nil
+        end
+      end
+
+      context 'when a Task list with a completed item is totally replaced' do
+        before do
+          update_issue({ description: "- [ ] Task 1\n- [X] Task 2" })
+          update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" })
+        end
+
+        it 'does not create a system note referencing the position the old item' do
+          note = find_note('Marked the task **Two** as incomplete')
+
+          expect(note).to be_nil
+        end
+
+        it 'should not generate a new note at all' do
+          expect do
+            update_issue({ description: "- [ ] One\n- [ ] Two\n- [ ] Three" })
+          end.not_to change { Note.count }
+        end
+      end
+    end
+
   end
 end
diff --git a/spec/services/merge_requests/close_service_spec.rb b/spec/services/merge_requests/close_service_spec.rb
index b3cbfd4b5b8d8e9074b186300d10f41191a1a2cd..272f3897938a22b18e0634fdcfdcb96e2c76db07 100644
--- a/spec/services/merge_requests/close_service_spec.rb
+++ b/spec/services/merge_requests/close_service_spec.rb
@@ -18,7 +18,9 @@ describe MergeRequests::CloseService do
       before do
         allow(service).to receive(:execute_hooks)
 
-        @merge_request = service.execute(merge_request)
+        perform_enqueued_jobs do
+          @merge_request = service.execute(merge_request)
+        end
       end
 
       it { expect(@merge_request).to be_valid }
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index 7483f51de034d65d2b8fcfec1d6b02748fbc6b25..c0961ceb11edf40be9e5b7ead317530001bfbda1 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -17,8 +17,9 @@ describe MergeRequests::MergeService do
 
       before do
         allow(service).to receive(:execute_hooks)
-
-        service.execute(merge_request, 'Awesome message')
+        perform_enqueued_jobs do
+          service.execute(merge_request, 'Awesome message')
+        end
       end
 
       it { expect(merge_request).to be_valid }
diff --git a/spec/services/merge_requests/reopen_service_spec.rb b/spec/services/merge_requests/reopen_service_spec.rb
index 9401bc3b55893ab47e734083b10d2dbd6cead703..05146bf43f4f1e8e1c6967b368cfce7d54655d4a 100644
--- a/spec/services/merge_requests/reopen_service_spec.rb
+++ b/spec/services/merge_requests/reopen_service_spec.rb
@@ -19,7 +19,9 @@ describe MergeRequests::ReopenService do
         allow(service).to receive(:execute_hooks)
 
         merge_request.state = :closed
-        service.execute(merge_request)
+        perform_enqueued_jobs do
+          service.execute(merge_request)
+        end
       end
 
       it { expect(merge_request).to be_valid }
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index c75173c1452002a23d92fd0419d61b0764082531..d899b1f01d12c68d8bb7d3af025fcd98fa466153 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -3,7 +3,8 @@ require 'spec_helper'
 describe MergeRequests::UpdateService do
   let(:user) { create(:user) }
   let(:user2) { create(:user) }
-  let(:merge_request) { create(:merge_request, :simple, title: 'Old title') }
+  let(:user3) { create(:user) }
+  let(:merge_request) { create(:merge_request, :simple, title: 'Old title', assignee_id: user3.id) }
   let(:project) { merge_request.project }
   let(:label) { create(:label) }
 
@@ -13,6 +14,17 @@ describe MergeRequests::UpdateService do
   end
 
   describe 'execute' do
+    def find_note(starting_with)
+      @merge_request.notes.find do |note|
+        note && note.note.start_with?(starting_with)
+      end
+    end
+
+    def update_merge_request(opts)
+      @merge_request = MergeRequests::UpdateService.new(project, user, opts).execute(merge_request)
+      @merge_request.reload
+    end
+
     context 'valid params' do
       let(:opts) do
         {
@@ -30,8 +42,10 @@ describe MergeRequests::UpdateService do
       before do
         allow(service).to receive(:execute_hooks)
 
-        @merge_request = service.execute(merge_request)
-        @merge_request.reload
+        perform_enqueued_jobs do
+          @merge_request = service.execute(merge_request)
+          @merge_request.reload
+        end
       end
 
       it { expect(@merge_request).to be_valid }
@@ -47,18 +61,14 @@ describe MergeRequests::UpdateService do
                                with(@merge_request, 'update')
       end
 
-      it 'should send email to user2 about assign of new merge_request' do
-        email = ActionMailer::Base.deliveries.last
-        expect(email.to.first).to eq(user2.email)
+      it 'should send email to user2 about assign of new merge request and email to user3 about merge request unassignment' do
+        deliveries = ActionMailer::Base.deliveries
+        email = deliveries.last
+        recipients = deliveries.last(2).map(&:to).flatten
+        expect(recipients).to include(user2.email, user3.email)
         expect(email.subject).to include(merge_request.title)
       end
 
-      def find_note(starting_with)
-        @merge_request.notes.find do |note|
-          note && note.note.start_with?(starting_with)
-        end
-      end
-
       it 'should create system note about merge_request reassign' do
         note = find_note('Reassigned to')
 
@@ -87,5 +97,39 @@ describe MergeRequests::UpdateService do
         expect(note.note).to eq 'Target branch changed from `master` to `target`'
       end
     end
+
+    context 'when MergeRequest has tasks' do
+      before { update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" }) }
+
+      it { expect(@merge_request.tasks?).to eq(true) }
+
+      context 'when tasks are marked as completed' do
+        before { update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" }) }
+
+        it 'creates system note about task status change' do
+          note1 = find_note('Marked the task **Task 1** as completed')
+          note2 = find_note('Marked the task **Task 2** as completed')
+
+          expect(note1).not_to be_nil
+          expect(note2).not_to be_nil
+        end
+      end
+
+      context 'when tasks are marked as incomplete' do
+        before do
+          update_merge_request({ description: "- [x] Task 1\n- [X] Task 2" })
+          update_merge_request({ description: "- [ ] Task 1\n- [ ] Task 2" })
+        end
+
+        it 'creates system note about task status change' do
+          note1 = find_note('Marked the task **Task 1** as incomplete')
+          note2 = find_note('Marked the task **Task 2** as incomplete')
+
+          expect(note1).not_to be_nil
+          expect(note2).not_to be_nil
+        end
+      end
+    end
+
   end
 end
diff --git a/spec/services/milestones/close_service_spec.rb b/spec/services/milestones/close_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..034c0f22e12d9c7d7b008587eecfdf36d7972079
--- /dev/null
+++ b/spec/services/milestones/close_service_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe Milestones::CloseService do
+  let(:user) { create(:user) }
+  let(:project) { create(:project) }
+  let(:milestone) { create(:milestone, title: "Milestone v1.2", project: project) }
+
+  before do
+    project.team << [user, :master]
+  end
+
+  describe :execute do
+    before do
+      Milestones::CloseService.new(project, user, {}).execute(milestone)
+    end
+
+    it { expect(milestone).to be_valid }
+    it { expect(milestone).to be_closed }
+
+    describe :event do
+      let(:event) { Event.first }
+
+      it { expect(event.milestone).to be_truthy }
+      it { expect(event.target).to eq(milestone) }
+      it { expect(event.action_name).to eq('closed') }
+    end
+  end
+end
diff --git a/spec/services/milestones/create_service_spec.rb b/spec/services/milestones/create_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..757c9a226d80d1c9057c928427c3f6d385ee6504
--- /dev/null
+++ b/spec/services/milestones/create_service_spec.rb
@@ -0,0 +1,24 @@
+require 'spec_helper'
+
+describe Milestones::CreateService do
+  let(:project) { create(:empty_project) }
+  let(:user) { create(:user) }
+
+  describe :execute do
+    context "valid params" do
+      before do
+        project.team << [user, :master]
+
+        opts = {
+          title: 'v2.1.9',
+          description: 'Patch release to fix security issue'
+        }
+
+        @milestone = Milestones::CreateService.new(project, user, opts).execute
+      end
+
+      it { expect(@milestone).to be_valid }
+      it { expect(@milestone.title).to eq('v2.1.9') }
+    end
+  end
+end
diff --git a/spec/services/milestones/group_service_spec.rb b/spec/services/milestones/group_service_spec.rb
deleted file mode 100644
index 74eb0f99e0f29e713c49fd27945bf573fbf13816..0000000000000000000000000000000000000000
--- a/spec/services/milestones/group_service_spec.rb
+++ /dev/null
@@ -1,70 +0,0 @@
-require 'spec_helper'
-
-describe Milestones::GroupService do
-  let(:user) { create(:user) }
-  let(:user2) { create(:user) }
-  let(:group) { create(:group) }
-  let(:project1) { create(:project, group: group) }
-  let(:project2) { create(:project, path: 'gitlab-ci', group: group) }
-  let(:project3) { create(:project, path: 'cookbook-gitlab', group: group) }
-  let(:milestone1_project1) { create(:milestone, title: "Milestone v1.2", project: project1) }
-  let(:milestone1_project2) { create(:milestone, title: "Milestone v1.2", project: project2) }
-  let(:milestone1_project3) { create(:milestone, title: "Milestone v1.2", project: project3) }
-  let(:milestone2_project1) { create(:milestone, title: "VD-123", project: project1) }
-  let(:milestone2_project2) { create(:milestone, title: "VD-123", project: project2) }
-  let(:milestone2_project3) { create(:milestone, title: "VD-123", project: project3) }
-
-  describe 'execute' do
-    context 'with valid projects' do
-      before do
-        milestones =
-          [ 
-            milestone1_project1,
-            milestone1_project2,
-            milestone1_project3,
-            milestone2_project1,
-            milestone2_project2,
-            milestone2_project3
-          ]
-        @group_milestones = Milestones::GroupService.new(milestones).execute
-      end
-
-      it 'should have all project milestones' do
-        expect(@group_milestones.count).to eq(2)
-      end
-
-      it 'should have all project milestones titles' do
-        expect(@group_milestones.map { |group_milestone| group_milestone.title }).to match_array(['Milestone v1.2', 'VD-123'])
-      end
-
-      it 'should have all project milestones' do
-        expect(@group_milestones.map { |group_milestone| group_milestone.milestones.count }.sum).to eq(6)
-      end
-    end
-  end
-
-  describe 'milestone' do
-    context 'with valid title' do
-      before do
-        milestones =
-          [ 
-            milestone1_project1,
-            milestone1_project2,
-            milestone1_project3,
-            milestone2_project1,
-            milestone2_project2,
-            milestone2_project3
-          ]
-        @group_milestones = Milestones::GroupService.new(milestones).milestone('Milestone v1.2')
-      end
-
-      it 'should have exactly one group milestone' do
-        expect(@group_milestones.title).to eq('Milestone v1.2')
-      end
-
-      it 'should have all project milestones with the same title' do
-        expect(@group_milestones.milestones.count).to eq(3)
-      end
-    end
-  end
-end
diff --git a/spec/services/notes/create_service_spec.rb b/spec/services/notes/create_service_spec.rb
index f2ea0805b2fb8466353c0200ccf2e4a8eeb2871e..cc38d2577928e041ff7788df15d3fa9e244560bb 100644
--- a/spec/services/notes/create_service_spec.rb
+++ b/spec/services/notes/create_service_spec.rb
@@ -24,4 +24,38 @@ describe Notes::CreateService do
       it { expect(@note.note).to eq('Awesome comment') }
     end
   end
+
+  describe "award emoji" do
+    before do
+      project.team << [user, :master]
+    end
+
+    it "creates emoji note" do
+      opts = {
+        note: ':smile: ',
+        noteable_type: 'Issue',
+        noteable_id: issue.id
+      }
+
+      @note = Notes::CreateService.new(project, user, opts).execute
+
+      expect(@note).to be_valid
+      expect(@note.note).to eq('smile')
+      expect(@note.is_award).to be_truthy
+    end
+
+    it "creates regular note if emoji name is invalid" do
+      opts = {
+        note: ':smile: moretext: ',
+        noteable_type: 'Issue',
+        noteable_id: issue.id
+      }
+
+      @note = Notes::CreateService.new(project, user, opts).execute
+
+      expect(@note).to be_valid
+      expect(@note.note).to eq(opts[:note])
+      expect(@note.is_award).to be_falsy
+    end
+  end
 end
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 520140917aae2e9f588df8aa5a4a93a1f954c96a..a4e2b2953ccce72d971d250554443a2368430000 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -3,6 +3,12 @@ require 'spec_helper'
 describe NotificationService do
   let(:notification) { NotificationService.new }
 
+  around(:each) do |example|
+    perform_enqueued_jobs do
+      example.run
+    end
+  end
+
   describe 'Keys' do
     describe :new_key do
       let!(:key) { create(:personal_key) }
@@ -10,8 +16,7 @@ describe NotificationService do
       it { expect(notification.new_key(key)).to be_truthy }
 
       it 'should sent email to key owner' do
-        expect(Notify).to receive(:new_ssh_key_email).with(key.id)
-        notification.new_key(key)
+        expect{ notification.new_key(key) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
       end
     end
   end
@@ -23,8 +28,7 @@ describe NotificationService do
       it { expect(notification.new_email(email)).to be_truthy }
 
       it 'should send email to email owner' do
-        expect(Notify).to receive(:new_email_email).with(email.id)
-        notification.new_email(email)
+        expect{ notification.new_email(email) }.to change{ ActionMailer::Base.deliveries.size }.by(1)
       end
     end
   end
@@ -47,18 +51,20 @@ describe NotificationService do
         it do
           add_users_with_subscription(note.project, issue)
 
-          should_email(@u_watcher.id)
-          should_email(note.noteable.author_id)
-          should_email(note.noteable.assignee_id)
-          should_email(@u_mentioned.id)
-          should_email(@subscriber.id)
-          should_not_email(note.author_id)
-          should_not_email(@u_participating.id)
-          should_not_email(@u_disabled.id)
-          should_not_email(@unsubscriber.id)
-          should_not_email(@u_outsider_mentioned)
+          ActionMailer::Base.deliveries.clear
 
           notification.new_note(note)
+
+          should_email(@u_watcher)
+          should_email(note.noteable.author)
+          should_email(note.noteable.assignee)
+          should_email(@u_mentioned)
+          should_email(@subscriber)
+          should_not_email(note.author)
+          should_not_email(@u_participating)
+          should_not_email(@u_disabled)
+          should_not_email(@unsubscriber)
+          should_not_email(@u_outsider_mentioned)
         end
 
         it 'filters out "mentioned in" notes' do
@@ -82,26 +88,20 @@ describe NotificationService do
           group_member = note.project.group.group_members.find_by_user_id(@u_watcher.id)
           group_member.notification_level = Notification::N_GLOBAL
           group_member.save
+          ActionMailer::Base.deliveries.clear
         end
 
         it do
-          should_email(note.noteable.author_id)
-          should_email(note.noteable.assignee_id)
-          should_email(@u_mentioned.id)
-          should_not_email(@u_watcher.id)
-          should_not_email(note.author_id)
-          should_not_email(@u_participating.id)
-          should_not_email(@u_disabled.id)
           notification.new_note(note)
-        end
-      end
 
-      def should_email(user_id)
-        expect(Notify).to receive(:note_issue_email).with(user_id, note.id)
-      end
-
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id)
+          should_email(note.noteable.author)
+          should_email(note.noteable.assignee)
+          should_email(@u_mentioned)
+          should_not_email(@u_watcher)
+          should_not_email(note.author)
+          should_not_email(@u_participating)
+          should_not_email(@u_disabled)
+        end
       end
     end
 
@@ -113,24 +113,26 @@ describe NotificationService do
 
       before do
         build_team(note.project)
+        ActionMailer::Base.deliveries.clear
       end
 
       describe :new_note do
         it do
+          notification.new_note(note)
+
           # Notify all team members
           note.project.team.members.each do |member|
             # User with disabled notification should not be notified
             next if member.id == @u_disabled.id
-            should_email(member.id)
+            should_email(member)
           end
-          should_email(note.noteable.author_id)
-          should_email(note.noteable.assignee_id)
 
-          should_not_email(note.author_id)
-          should_not_email(@u_mentioned.id)
-          should_not_email(@u_disabled.id)
-          should_not_email(@u_not_mentioned.id)
-          notification.new_note(note)
+          should_email(note.noteable.author)
+          should_email(note.noteable.assignee)
+          should_not_email(note.author)
+          should_email(@u_mentioned)
+          should_not_email(@u_disabled)
+          should_email(@u_not_mentioned)
         end
 
         it 'filters out "mentioned in" notes' do
@@ -140,14 +142,6 @@ describe NotificationService do
           notification.new_note(mentioned_note)
         end
       end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:note_issue_email).with(user_id, note.id)
-      end
-
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:note_issue_email).with(user_id, note.id)
-      end
     end
 
     context 'commit note' do
@@ -156,43 +150,38 @@ describe NotificationService do
 
       before do
         build_team(note.project)
+        ActionMailer::Base.deliveries.clear
         allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer)
       end
 
-      describe :new_note do
+      describe :new_note, :perform_enqueued_jobs do
         it do
-          should_email(@u_committer.id, note)
-          should_email(@u_watcher.id, note)
-          should_not_email(@u_mentioned.id, note)
-          should_not_email(note.author_id, note)
-          should_not_email(@u_participating.id, note)
-          should_not_email(@u_disabled.id, note)
           notification.new_note(note)
+
+          should_email(@u_committer)
+          should_email(@u_watcher)
+          should_not_email(@u_mentioned)
+          should_not_email(note.author)
+          should_not_email(@u_participating)
+          should_not_email(@u_disabled)
         end
 
         it do
           note.update_attribute(:note, '@mention referenced')
-          should_email(@u_committer.id, note)
-          should_email(@u_watcher.id, note)
-          should_email(@u_mentioned.id, note)
-          should_not_email(note.author_id, note)
-          should_not_email(@u_participating.id, note)
-          should_not_email(@u_disabled.id, note)
           notification.new_note(note)
+
+          should_email(@u_committer)
+          should_email(@u_watcher)
+          should_email(@u_mentioned)
+          should_not_email(note.author)
+          should_not_email(@u_participating)
+          should_not_email(@u_disabled)
         end
 
         it do
           @u_committer.update_attributes(notification_level: Notification::N_MENTION)
-          should_not_email(@u_committer.id, note)
           notification.new_note(note)
-        end
-
-        def should_email(user_id, n)
-          expect(Notify).to receive(:note_commit_email).with(user_id, n.id)
-        end
-
-        def should_not_email(user_id, n)
-          expect(Notify).not_to receive(:note_commit_email).with(user_id, n.id)
+          should_not_email(@u_committer)
         end
       end
     end
@@ -205,99 +194,69 @@ describe NotificationService do
     before do
       build_team(issue.project)
       add_users_with_subscription(issue.project, issue)
+      ActionMailer::Base.deliveries.clear
     end
 
     describe :new_issue do
       it do
-        should_email(issue.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_not_email(@u_mentioned.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.new_issue(issue, @u_disabled)
+
+        should_email(issue.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_not_email(@u_mentioned)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
 
       it do
         issue.assignee.update_attributes(notification_level: Notification::N_MENTION)
-        should_not_email(issue.assignee_id)
         notification.new_issue(issue, @u_disabled)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:new_issue_email).with(user_id, issue.id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:new_issue_email).with(user_id, issue.id)
+        should_not_email(issue.assignee)
       end
     end
 
     describe :reassigned_issue do
       it 'should email new assignee' do
-        should_email(issue.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
-
         notification.reassigned_issue(issue, @u_disabled)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:reassigned_issue_email).with(user_id, issue.id, nil, @u_disabled.id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:reassigned_issue_email).with(user_id, issue.id, issue.assignee_id, @u_disabled.id)
+        should_email(issue.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :close_issue do
       it 'should sent email to issue assignee and issue author' do
-        should_email(issue.assignee_id)
-        should_email(issue.author_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
-
         notification.close_issue(issue, @u_disabled)
-      end
 
-      def should_email(user_id)
-        expect(Notify).to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
-      end
-
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:closed_issue_email).with(user_id, issue.id, @u_disabled.id)
+        should_email(issue.assignee)
+        should_email(issue.author)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :reopen_issue do
       it 'should send email to issue assignee and issue author' do
-        should_email(issue.assignee_id)
-        should_email(issue.author_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
-
         notification.reopen_issue(issue, @u_disabled)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:issue_status_changed_email).with(user_id, issue.id, 'reopened', @u_disabled.id)
+        should_email(issue.assignee)
+        should_email(issue.author)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
       end
     end
   end
@@ -309,108 +268,74 @@ describe NotificationService do
     before do
       build_team(merge_request.target_project)
       add_users_with_subscription(merge_request.target_project, merge_request)
+      ActionMailer::Base.deliveries.clear
     end
 
     describe :new_merge_request do
       it do
-        should_email(merge_request.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.new_merge_request(merge_request, @u_disabled)
-      end
 
-      def should_email(user_id)
-        expect(Notify).to receive(:new_merge_request_email).with(user_id, merge_request.id)
-      end
-
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:new_merge_request_email).with(user_id, merge_request.id)
+        should_email(merge_request.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :reassigned_merge_request do
       it do
-        should_email(merge_request.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.reassigned_merge_request(merge_request, merge_request.author)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, nil, merge_request.author_id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:reassigned_merge_request_email).with(user_id, merge_request.id, merge_request.assignee_id, merge_request.author_id)
+        should_email(merge_request.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :closed_merge_request do
       it do
-        should_email(merge_request.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.close_mr(merge_request, @u_disabled)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:closed_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+        should_email(merge_request.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :merged_merge_request do
       it do
-        should_email(merge_request.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.merge_mr(merge_request, @u_disabled)
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:merged_merge_request_email).with(user_id, merge_request.id, @u_disabled.id)
+        should_email(merge_request.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
 
     describe :reopen_merge_request do
       it do
-        should_email(merge_request.assignee_id)
-        should_email(@u_watcher.id)
-        should_email(@u_participant_mentioned.id)
-        should_email(@subscriber.id)
-        should_not_email(@unsubscriber.id)
-        should_not_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.reopen_mr(merge_request, @u_disabled)
-      end
 
-      def should_email(user_id)
-        expect(Notify).to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
-      end
-
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:merge_request_status_email).with(user_id, merge_request.id, 'reopened', @u_disabled.id)
+        should_email(merge_request.assignee)
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
   end
@@ -420,22 +345,16 @@ describe NotificationService do
 
     before do
       build_team(project)
+      ActionMailer::Base.deliveries.clear
     end
 
     describe :project_was_moved do
       it do
-        should_email(@u_watcher.id)
-        should_email(@u_participating.id)
-        should_not_email(@u_disabled.id)
         notification.project_was_moved(project, "gitlab/gitlab")
-      end
-
-      def should_email(user_id)
-        expect(Notify).to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab")
-      end
 
-      def should_not_email(user_id)
-        expect(Notify).not_to receive(:project_was_moved_email).with(project.id, user_id, "gitlab/gitlab")
+        should_email(@u_watcher)
+        should_email(@u_participating)
+        should_not_email(@u_disabled)
       end
     end
   end
@@ -469,4 +388,18 @@ describe NotificationService do
     issuable.subscriptions.create(user: @subscriber, subscribed: true)
     issuable.subscriptions.create(user: @unsubscriber, subscribed: false)
   end
+
+  def sent_to_user?(user)
+    ActionMailer::Base.deliveries.any? do |message|
+      message.to.include?(user.email)
+    end
+  end
+
+  def should_email(user)
+    expect(sent_to_user?(user)).to be_truthy
+  end
+
+  def should_not_email(user)
+    expect(sent_to_user?(user)).to be_falsey
+  end
 end
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 25277f07482485dc312650ce14567cc447f3bc51..e81c4edb7d83ea7f08218cea5964b9053f34d39a 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -70,6 +70,28 @@ describe Projects::CreateService do
       end
     end
 
+    context 'builds_enabled global setting' do
+      let(:project) { create_project(@user, @opts) }
+
+      subject { project.builds_enabled? }
+
+      context 'global builds_enabled false does not enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: false)
+        end
+
+        it { is_expected.to be_falsey }
+      end
+
+      context 'global builds_enabled true does enable CI by default' do
+        before do
+          @opts.merge!(builds_enabled: true)
+        end
+
+        it { is_expected.to be_truthy }
+      end
+    end
+
     context 'restricted visibility level' do
       before do
         stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
diff --git a/spec/services/projects/fork_service_spec.rb b/spec/services/projects/fork_service_spec.rb
index 65a8c81204d3c3a509b4c302934c05cd9fb9b539..1feba6ce0489e4cec3a31ed0f73b7a533b05d2bd 100644
--- a/spec/services/projects/fork_service_spec.rb
+++ b/spec/services/projects/fork_service_spec.rb
@@ -25,13 +25,6 @@ describe Projects::ForkService do
       end
     end
 
-    context 'fork project failure' do
-      it "fails due to transaction failure" do
-        @to_project = fork_project(@from_project, @to_user, false)
-        expect(@to_project.import_failed?)
-      end
-    end
-
     context 'project already exists' do
       it "should fail due to validation, not transaction failure" do
         @existing_project = create(:project, creator_id: @to_user.id, name: @from_project.name, namespace: @to_namespace)
@@ -46,7 +39,7 @@ describe Projects::ForkService do
       it "fork and enable CI for fork" do
         @from_project.enable_ci
         @to_project = fork_project(@from_project, @to_user)
-        expect(@to_project.gitlab_ci?).to be_truthy
+        expect(@to_project.builds_enabled?).to be_truthy
       end
     end
   end
@@ -66,7 +59,7 @@ describe Projects::ForkService do
 
     context 'fork project for group' do
       it 'group owner successfully forks project into the group' do
-        to_project = fork_project(@project, @group_owner, true, @opts)
+        to_project = fork_project(@project, @group_owner, @opts)
         expect(to_project.owner).to       eq(@group)
         expect(to_project.namespace).to   eq(@group)
         expect(to_project.name).to        eq(@project.name)
@@ -78,7 +71,7 @@ describe Projects::ForkService do
 
     context 'fork project for group when user not owner' do
       it 'group developer should fail to fork project into the group' do
-        to_project = fork_project(@project, @developer, true, @opts)
+        to_project = fork_project(@project, @developer, @opts)
         expect(to_project.errors[:namespace]).to eq(['is not valid'])
       end
     end
@@ -87,7 +80,7 @@ describe Projects::ForkService do
       it 'should fail due to validation, not transaction failure' do
         existing_project = create(:project, name: @project.name,
                                             namespace: @group)
-        to_project = fork_project(@project, @group_owner, true, @opts)
+        to_project = fork_project(@project, @group_owner, @opts)
         expect(existing_project.persisted?).to be_truthy
         expect(to_project.errors[:name]).to eq(['has already been taken'])
         expect(to_project.errors[:path]).to eq(['has already been taken'])
@@ -95,8 +88,8 @@ describe Projects::ForkService do
     end
   end
 
-  def fork_project(from_project, user, fork_success = true, params = {})
-    allow(RepositoryForkWorker).to receive(:perform_async).and_return(fork_success)
+  def fork_project(from_project, user, params = {})
+    allow(RepositoryForkWorker).to receive(:perform_async).and_return(true)
     Projects::ForkService.new(from_project, user, params).execute
   end
 end
diff --git a/spec/services/update_release_service_spec.rb b/spec/services/update_release_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..93368c45b88c4511551ee8cac175506dc417ba94
--- /dev/null
+++ b/spec/services/update_release_service_spec.rb
@@ -0,0 +1,34 @@
+require 'spec_helper'
+
+describe UpdateReleaseService do
+  let(:project) { create(:project) }
+  let(:user) { create(:user) }
+  let(:tag_name) { project.repository.tag_names.first }
+  let(:description) { 'Awesome release!' }
+  let(:new_description) { 'The best release!' }
+  let(:service) { UpdateReleaseService.new(project, user) }
+
+  context 'with an existing release' do
+    let(:create_service) { CreateReleaseService.new(project, user) }
+
+    before do
+      create_service.execute(tag_name, description)
+    end
+
+    it 'successfully updates an existing release' do
+      result = service.execute(tag_name, new_description)
+      expect(result[:status]).to eq(:success)
+      expect(project.releases.find_by(tag: tag_name).description).to eq(new_description)
+    end
+  end
+
+  it 'raises an error if the tag does not exist' do
+    result = service.execute("foobar", description)
+    expect(result[:status]).to eq(:error)
+  end
+
+  it 'raises an error if the release does not exist' do
+    result = service.execute(tag_name, description)
+    expect(result[:status]).to eq(:error)
+  end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 2be13bb3e6a0dd95d1c8962724b12e18517011e0..0225a0ee53f27a87b9ad87b6a3bc4c597a3e494c 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -31,6 +31,7 @@ RSpec.configure do |config|
   config.include StubConfiguration
   config.include RelativeUrl,         type: feature
   config.include TestEnv
+  config.include ActiveJob::TestHelper
   config.include StubGitlabCalls
   config.include StubGitlabData
   config.include BenchmarkMatchers, benchmark: true
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 06559c3925d5b8e08e9c0da253210db2b8d92355..63bed2414dfbcfac332e615889ecaa753ec6328c 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -16,7 +16,7 @@ describe 'gitlab:app namespace rake task' do
   end
 
   def reenable_backup_sub_tasks
-    %w{db repo uploads builds artifacts}.each do |subtask|
+    %w{db repo uploads builds artifacts lfs}.each do |subtask|
       Rake::Task["gitlab:backup:#{subtask}:create"].reenable
     end
   end
@@ -49,7 +49,7 @@ describe 'gitlab:app namespace rake task' do
           to raise_error(SystemExit)
       end
 
-      it 'should invoke restoration on mach' do
+      it 'should invoke restoration on match' do
         allow(YAML).to receive(:load_file).
           and_return({ gitlab_version: gitlab_version })
         expect(Rake::Task["gitlab:backup:db:restore"]).to receive(:invoke)
@@ -57,6 +57,7 @@ describe 'gitlab:app namespace rake task' do
         expect(Rake::Task["gitlab:backup:builds:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:backup:uploads:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive(:invoke)
+        expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive(:invoke)
         expect(Rake::Task["gitlab:shell:setup"]).to receive(:invoke)
         expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
       end
@@ -114,7 +115,7 @@ describe 'gitlab:app namespace rake task' do
 
     it 'should set correct permissions on the tar contents' do
       tar_contents, exit_status = Gitlab::Popen.popen(
-        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz}
+        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz}
       )
       expect(exit_status).to eq(0)
       expect(tar_contents).to match('db/')
@@ -122,12 +123,13 @@ describe 'gitlab:app namespace rake task' do
       expect(tar_contents).to match('repositories/')
       expect(tar_contents).to match('builds.tar.gz')
       expect(tar_contents).to match('artifacts.tar.gz')
+      expect(tar_contents).to match('lfs.tar.gz')
       expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/)
     end
 
     it 'should delete temp directories' do
       temp_dirs = Dir.glob(
-        File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts}')
+        File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs}')
       )
 
       expect(temp_dirs).to be_empty
@@ -149,7 +151,7 @@ describe 'gitlab:app namespace rake task' do
       # Redirect STDOUT and run the rake task
       orig_stdout = $stdout
       $stdout = StringIO.new
-      ENV["SKIP"] = "repositories"
+      ENV["SKIP"] = "repositories,uploads"
       run_rake_task('gitlab:backup:create')
       $stdout = orig_stdout
 
@@ -163,13 +165,14 @@ describe 'gitlab:app namespace rake task' do
 
     it "does not contain skipped item" do
       tar_contents, _exit_status = Gitlab::Popen.popen(
-        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz}
+        %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz}
       )
 
       expect(tar_contents).to match('db/')
       expect(tar_contents).to match('uploads.tar.gz')
       expect(tar_contents).to match('builds.tar.gz')
       expect(tar_contents).to match('artifacts.tar.gz')
+      expect(tar_contents).to match('lfs.tar.gz')
       expect(tar_contents).not_to match('repositories/')
     end
 
@@ -180,8 +183,10 @@ describe 'gitlab:app namespace rake task' do
 
       expect(Rake::Task["gitlab:backup:db:restore"]).to receive :invoke
       expect(Rake::Task["gitlab:backup:repo:restore"]).not_to receive :invoke
+      expect(Rake::Task["gitlab:backup:uploads:restore"]).not_to receive :invoke
       expect(Rake::Task["gitlab:backup:builds:restore"]).to receive :invoke
       expect(Rake::Task["gitlab:backup:artifacts:restore"]).to receive :invoke
+      expect(Rake::Task["gitlab:backup:lfs:restore"]).to receive :invoke
       expect(Rake::Task["gitlab:shell:setup"]).to receive :invoke
       expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
     end
diff --git a/spec/workers/email_receiver_worker_spec.rb b/spec/workers/email_receiver_worker_spec.rb
index 65a8d7d919748131589ec6e5781f4d6502d44041..de40a6f78af61754e0206c7aab04684a450c7cf4 100644
--- a/spec/workers/email_receiver_worker_spec.rb
+++ b/spec/workers/email_receiver_worker_spec.rb
@@ -21,12 +21,14 @@ describe EmailReceiverWorker do
       end
 
       it "sends out a rejection email" do
-        described_class.new.perform(raw_message)
-
-        email = ActionMailer::Base.deliveries.last
-        expect(email).not_to be_nil
-        expect(email.to).to eq(["jake@adventuretime.ooo"])
-        expect(email.subject).to include("Rejected")
+        perform_enqueued_jobs do
+          described_class.new.perform(raw_message)
+
+          email = ActionMailer::Base.deliveries.last
+          expect(email).not_to be_nil
+          expect(email.to).to eq(["jake@adventuretime.ooo"])
+          expect(email.subject).to include("Rejected")
+        end
       end
     end
   end
diff --git a/spec/workers/repository_fork_worker_spec.rb b/spec/workers/repository_fork_worker_spec.rb
index aa0311069689dd191102a093a473febbfeefce72..245f066df1f6a016baa738e15e3c1642133e9a6f 100644
--- a/spec/workers/repository_fork_worker_spec.rb
+++ b/spec/workers/repository_fork_worker_spec.rb
@@ -12,7 +12,6 @@ describe RepositoryForkWorker do
                                                    project.path_with_namespace,
                                                    fork_project.namespace.path).
                                                    and_return(true)
-      expect(ProjectCacheWorker).to receive(:perform_async)
 
       subject.perform(project.id,
                       project.path_with_namespace,