diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index c23a7a3bf0ecd5615d8282a557349c1cdec2b2d1..ac8390074f4c82a6c40960a892a69e4c871eaf1e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -8,7 +8,7 @@ before_script:
   - touch log/application.log
   - touch log/test.log
   - bundle install --without postgres production --jobs $(nproc)  "${FLAGS[@]}"
-  - bundle exec rake db:reset db:create RAILS_ENV=test
+  - RAILS_ENV=test bundle exec rake db:drop db:create db:schema:load db:migrate
 
 spec:feature:
   script:
@@ -118,7 +118,7 @@ flay:
     - mysql
 
 bundler:audit:
-  script: 
+  script:
     - "bundle exec bundle-audit update"
     - "bundle exec bundle-audit check"
   tags:
diff --git a/CHANGELOG b/CHANGELOG
index 9ff4820c12c9bc4290f63e5cb6f032ab66033785..6985c1fa46e0d09f531560549f0eabd238b17607 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,6 +1,24 @@
 Please view this file on the master branch, on stable branches it's out of date.
 
+v 8.5.0 (unreleased)
+  - Add "visibility" flag to GET /projects api endpoint
+  - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push
+  - New UI for pagination
+
 v 8.4.0 (unreleased)
+  - Allow LDAP users to change their email if it was not set by the LDAP server
+  - Ensure Gravatar host looks like an actual host
+  - Consider re-assign as a mention from a notification point of view
+  - Add pagination headers to already paginated API resources
+  - Properly generate diff of orphan commits, like the first commit in a repository
+  - Improve the consistency of commit titles, branch names, tag names, issue/MR titles, on their respective project pages
+  - Autocomplete data is now always loaded, instead of when focusing a comment text area
+  - Improved performance of finding issues for an entire group
+  - Added custom application performance measuring system powered by InfluxDB
+  - Add syntax highlighting to diffs
+  - Gracefully handle invalid UTF-8 sequences in Markdown links (Stan Hu)
+  - Bump fog to 1.36.0 (Stan Hu)
+  - Add user's last used IP addresses to admin page (Stan Hu)
   - Add housekeeping function to project settings page
   - The default GitLab logo now acts as a loading indicator
   - Fix caching issue where build status was not updating in project dashboard (Stan Hu)
@@ -8,8 +26,11 @@ v 8.4.0 (unreleased)
   - Fix missing date of month in network graph when commits span a month (Stan Hu)
   - Expire view caches when application settings change (e.g. Gravatar disabled) (Stan Hu)
   - Don't notify users twice if they are both project watchers and subscribers (Stan Hu)
+  - Remove gray background from layout in UI
+  - Fix signup for OAuth providers that don't provide a name
   - Implement new UI for group page
   - Implement search inside emoji picker
+  - Let the CI runner know about builds that this build depends on
   - Add API support for looking up a user by username (Stan Hu)
   - Add project permissions to all project API endpoints (Stan Hu)
   - Link to milestone in "Milestone changed" system note
@@ -26,6 +47,9 @@ v 8.4.0 (unreleased)
   - Show 'All' tab by default in the builds page
   - Add Open Graph and Twitter Card data to all pages
   - Fix API project lookups when querying with a namespace with dots (Stan Hu)
+  - Enable forcing Two-Factor authentication sitewide, with optional grace period
+  - Import GitHub Pull Requests into GitLab
+  - Change single user API endpoint to return more detailed data (Michael Potthoff)
   - Update version check images to use SVG
   - Validate README format before displaying
   - Enable Microsoft Azure OAuth2 support (Janis Meybohm)
@@ -33,21 +57,47 @@ v 8.4.0 (unreleased)
   - Add file finder feature in tree view (Kyungchul Shin)
   - Ajax filter by message for commits page
   - API: Add support for deleting a tag via the API (Robert Schilling)
-
-v 8.3.3 (unreleased)
+  - Allow subsequent validations in CI Linter
+  - Show referenced MRs & Issues only when the current viewer can access them
+  - Fix Encoding::CompatibilityError bug when markdown content has some complex URL (Jason Lee)
+  - Add API support for managing project's builds
+  - Add API support for managing project's build triggers
+  - Add API support for managing project's build variables
+  - Allow broadcast messages to be edited
+  - Autosize Markdown textareas
+  - Import GitHub wiki into GitLab
+  - Add reporters ability to download and browse build artifacts (Andrew Johnson)
+  - Autofill referring url in message box when reporting user abuse.
+  - Remove leading comma on award emoji when the user is the first to award the emoji (Zeger-Jan van de Weg)
+  - Add build artifacts browser
+  - Improve UX in builds artifacts browser
+  - Increase default size of `data` column in `events` table when using MySQL
+  - Expose button to CI Lint tool on project builds page
+  - Fix: Creator should be added as a master of the project on creation
+  - Added X-GitLab-... headers to emails from CI and Email On Push services (Anton Baklanov)
+  - Add IP check against DNSBLs at account sign-up
+  - Added cache:key to .gitlab-ci.yml allowing to fine tune the caching
+
+v 8.3.4
+  - Use gitlab-workhorse 0.5.4 (fixes API routing bug)
+
+v 8.3.3
   - Preserve CE behavior with JIRA integration by only calling API if URL is set
   - Fix duplicated branch creation/deletion events when using Web UI (Stan Hu)
+  - Add configurable LDAP server query timeout
   - Get "Merge when build succeeds" to work when commits were pushed to MR target branch while builds were running
   - Suppress e-mails on failed builds if allow_failure is set (Stan Hu)
   - Fix project transfer e-mail sending incorrect paths in e-mail notification (Stan Hu)
+  - Better support for referencing and closing issues in Asana service (Mike Wyatt)
   - Enable "Add key" button when user fills in a proper key (Stan Hu)
   - Fix error in processing reply-by-email messages (Jason Lee)
   - Fix Error 500 when visiting build page of project with nil runners_token (Stan Hu)
+  - Use WOFF versions of SourceSansPro fonts
+  - Fix regression when builds were not generated for tags created through web/api interface
+  - Fix: maintain milestone filter between Open and Closed tabs (Greg Smethells)
+  - Fix missing artifacts and build traces for build created before 8.3
 
 v 8.3.2
-  - Change single user API endpoint to return more detailed data (Michael Potthoff)
-
-v 8.3.2 (unreleased)
   - Disable --follow in `git log` to avoid loading duplicate commit data in infinite scroll (Stan Hu)
   - Add support for Google reCAPTCHA in user registration
 
@@ -56,8 +106,6 @@ v 8.3.1
   - Fix Error 500 when doing a search in dashboard before visiting any project (Stan Hu)
   - Fix LDAP identity and user retrieval when special characters are used
   - Move Sidekiq-cron configuration to gitlab.yml
-  - Enable forcing Two-Factor authentication sitewide, with optional grace period
-  - Import GitHub Pull Requests into GitLab
 
 v 8.3.0
   - Bump rack-attack to 4.3.1 for security fix (Stan Hu)
@@ -65,6 +113,7 @@ v 8.3.0
   - Add open_issues_count to project API (Stan Hu)
   - Expand character set of usernames created by Omniauth (Corey Hinshaw)
   - Add button to automatically merge a merge request when the build succeeds (Zeger-Jan van de Weg)
+  - Add unsubscribe link in the email footer (Zeger-Jan van de Weg)
   - Provide better diagnostic message upon project creation errors (Stan Hu)
   - Bump devise to 3.5.3 to fix reset token expiring after account creation (Stan Hu)
   - Remove api credentials from link to build_page
@@ -78,6 +127,7 @@ v 8.3.0
   - Fix: Assignee selector is empty when 'Unassigned' is selected (Jose Corcuera)
   - Add rake tasks for git repository maintainance (Zeger-Jan van de Weg)
   - Fix 500 error when update group member permission
+  - Fix: As an admin, cannot add oneself as a member to a group/project
   - Trim leading and trailing whitespace of milestone and issueable titles (Jose Corcuera)
   - Recognize issue/MR/snippet/commit links as references
   - Backport JIRA features from EE to CE
@@ -139,7 +189,6 @@ v 8.2.2
   - 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: As an admin, cannot add oneself as a member to a group/project
   - Fix invalid links within projects dashboard header
   - Make current user the first user in assignee dropdown in issues detail page (Stan Hu)
   - Fix: duplicate email notifications on issue comments
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index b9c2b3d2f8e8242009998736c80c1303a90a2c9d..1eabbdc5cad0e0fbcc6eec6af135a4f3b2fec7a0 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -334,9 +334,9 @@ merge request:
 1.  [CoffeeScript](https://github.com/thoughtbot/guides/tree/master/style/coffeescript)
 1.  [Shell commands](doc/development/shell_commands.md) created by GitLab
     contributors to enhance security
-1.  [Markdown](http://www.cirosantilli.com/markdown-styleguide)
 1.  [Database Migrations](doc/development/migration_style_guide.md)
-1.  [Documentation styleguide](doc_styleguide.md)
+1.  [Markdown](http://www.cirosantilli.com/markdown-styleguide)
+1.  [Documentation styleguide](doc/development/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
diff --git a/GITLAB_WORKHORSE_VERSION b/GITLAB_WORKHORSE_VERSION
index 4b9fcbec101a6ff8ec68e0f95131ccda4861407f..ee6cdce3c29053ac99607147be5be250efa001bd 100644
--- a/GITLAB_WORKHORSE_VERSION
+++ b/GITLAB_WORKHORSE_VERSION
@@ -1 +1 @@
-0.5.1
+0.6.1
diff --git a/Gemfile b/Gemfile
index e9361f73e6e357b3069580cd492b938f9c53ce21..746d8e5d6dea823d2fdcfea08cbb3c7fb0812a00 100644
--- a/Gemfile
+++ b/Gemfile
@@ -18,7 +18,7 @@ gem "mysql2", '~> 0.3.16', group: :mysql
 gem "pg", '~> 0.18.2', group: :postgres
 
 # Authentication libraries
-gem 'devise',                 '~> 3.5.3'
+gem 'devise',                 '~> 3.5.4'
 gem 'devise-async',           '~> 0.9.0'
 gem 'doorkeeper',             '~> 2.2.0'
 gem 'omniauth',               '~> 1.2.2'
@@ -49,7 +49,7 @@ gem "browser", '~> 1.0.0'
 
 # Extracting information from a git repository
 # Provide access to Gitlab::Git library
-gem "gitlab_git", '~> 7.2.22'
+gem "gitlab_git", '~> 7.2.23'
 
 # LDAP Auth
 # GitLab fork with several improvements to original library. For full list of changes
@@ -80,7 +80,7 @@ gem "carrierwave", '~> 0.9.0'
 gem 'dropzonejs-rails', '~> 0.7.1'
 
 # for aws storage
-gem "fog", "~> 1.25.0"
+gem "fog", "~> 1.36.0"
 gem "unf", '~> 0.1.4'
 
 # Authorization
@@ -247,7 +247,7 @@ group :development, :test do
   gem 'byebug', platform: :mri
   gem 'pry-rails'
 
-  gem 'awesome_print', '~> 1.2.0'
+  gem 'awesome_print', '~> 1.2.0', require: false
   gem 'fuubar', '~> 2.0.0'
 
   gem 'database_cleaner', '~> 1.4.0'
@@ -293,6 +293,9 @@ end
 
 group :production do
   gem "gitlab_meta", '7.0'
+
+  # Sentry integration
+  gem 'sentry-raven'
 end
 
 gem "newrelic_rpm", '~> 3.9.4.245'
diff --git a/Gemfile.lock b/Gemfile.lock
index db0104eee84e77dcb3820cf74f24461a7b32f92c..5e0718af70fc21a304a847512c8cd73d47587fd3 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -157,7 +157,7 @@ GEM
       activerecord (>= 3.2.0, < 5.0)
     descendants_tracker (0.0.4)
       thread_safe (~> 0.3, >= 0.3.1)
-    devise (3.5.3)
+    devise (3.5.4)
       bcrypt (~> 3.0)
       orm_adapter (~> 0.1)
       railties (>= 3.2.6, < 5)
@@ -219,21 +219,45 @@ GEM
     flowdock (0.7.1)
       httparty (~> 0.7)
       multi_json
-    fog (1.25.0)
+    fog (1.36.0)
+      fog-aliyun (>= 0.1.0)
+      fog-atmos
+      fog-aws (>= 0.6.0)
       fog-brightbox (~> 0.4)
-      fog-core (~> 1.25)
+      fog-core (~> 1.32)
+      fog-dynect (~> 0.0.2)
+      fog-ecloud (~> 0.1)
+      fog-google (<= 0.1.0)
       fog-json
+      fog-local
+      fog-powerdns (>= 0.1.1)
       fog-profitbricks
       fog-radosgw (>= 0.0.2)
+      fog-riakcs
       fog-sakuracloud (>= 0.0.4)
+      fog-serverlove
       fog-softlayer
+      fog-storm_on_demand
       fog-terremark
       fog-vmfusion
       fog-voxel
+      fog-xenserver
       fog-xml (~> 0.1.1)
       ipaddress (~> 0.5)
       nokogiri (~> 1.5, >= 1.5.11)
-      opennebula
+    fog-aliyun (0.1.0)
+      fog-core (~> 1.27)
+      fog-json (~> 1.0)
+      ipaddress (~> 0.8)
+      xml-simple (~> 1.1)
+    fog-atmos (0.1.0)
+      fog-core
+      fog-xml
+    fog-aws (0.8.1)
+      fog-core (~> 1.27)
+      fog-json (~> 1.0)
+      fog-xml (~> 0.1)
+      ipaddress (~> 0.8)
     fog-brightbox (0.10.1)
       fog-core (~> 1.22)
       fog-json
@@ -242,21 +266,48 @@ GEM
       builder
       excon (~> 0.45)
       formatador (~> 0.2)
+    fog-dynect (0.0.2)
+      fog-core
+      fog-json
+      fog-xml
+    fog-ecloud (0.3.0)
+      fog-core
+      fog-xml
+    fog-google (0.1.0)
+      fog-core
+      fog-json
+      fog-xml
     fog-json (1.0.2)
       fog-core (~> 1.0)
       multi_json (~> 1.10)
+    fog-local (0.2.1)
+      fog-core (~> 1.27)
+    fog-powerdns (0.1.1)
+      fog-core (~> 1.27)
+      fog-json (~> 1.0)
+      fog-xml (~> 0.1)
     fog-profitbricks (0.0.5)
       fog-core
       fog-xml
       nokogiri
-    fog-radosgw (0.0.4)
+    fog-radosgw (0.0.5)
       fog-core (>= 1.21.0)
       fog-json
       fog-xml (>= 0.0.1)
-    fog-sakuracloud (1.5.0)
+    fog-riakcs (0.1.0)
+      fog-core
+      fog-json
+      fog-xml
+    fog-sakuracloud (1.7.5)
+      fog-core
+      fog-json
+    fog-serverlove (0.1.2)
+      fog-core
+      fog-json
+    fog-softlayer (1.0.3)
       fog-core
       fog-json
-    fog-softlayer (1.0.2)
+    fog-storm_on_demand (0.1.1)
       fog-core
       fog-json
     fog-terremark (0.1.0)
@@ -268,6 +319,9 @@ GEM
     fog-voxel (0.1.0)
       fog-core
       fog-xml
+    fog-xenserver (0.2.2)
+      fog-core
+      fog-xml
     fog-xml (0.1.2)
       fog-core
       nokogiri (~> 1.5, >= 1.5.11)
@@ -302,7 +356,7 @@ GEM
       posix-spawn (~> 0.3)
     gitlab_emoji (0.2.0)
       gemojione (~> 2.1)
-    gitlab_git (7.2.22)
+    gitlab_git (7.2.23)
       activesupport (~> 4.0)
       charlock_holmes (~> 0.7.3)
       github-linguist (~> 4.7.0)
@@ -377,7 +431,7 @@ GEM
     influxdb (0.2.3)
       cause
       json
-    ipaddress (0.8.0)
+    ipaddress (0.8.2)
     jquery-atwho-rails (1.3.2)
     jquery-rails (4.0.5)
       rails-dom-testing (~> 1.0)
@@ -492,10 +546,6 @@ GEM
       activesupport
       nokogiri (>= 1.4.4)
       omniauth (~> 1.0)
-    opennebula (4.14.2)
-      json
-      nokogiri
-      rbvmomi
     org-ruby (0.9.12)
       rubypants (~> 0.2)
     orm_adapter (0.5.0)
@@ -564,17 +614,13 @@ GEM
       thor (>= 0.18.1, < 2.0)
     rainbow (2.0.0)
     raindrops (0.15.0)
-    rake (10.4.2)
+    rake (10.5.0)
     raphael-rails (2.1.2)
     rb-fsevent (0.9.6)
     rb-inotify (0.9.5)
       ffi (>= 0.5.0)
     rblineprof (0.3.6)
       debugger-ruby_core_source (~> 1.3)
-    rbvmomi (1.8.2)
-      builder
-      nokogiri (>= 1.4.1)
-      trollop
     rdoc (3.12.2)
       json (~> 1.4)
     recaptcha (1.0.2)
@@ -602,8 +648,8 @@ GEM
     request_store (1.2.1)
     rerun (0.11.0)
       listen (~> 3.0)
-    responders (2.1.0)
-      railties (>= 4.2.0, < 5)
+    responders (2.1.1)
+      railties (>= 4.2.0, < 5.1)
     rest-client (1.8.0)
       http-cookie (>= 1.0.2, < 2.0)
       mime-types (>= 1.16, < 3.0)
@@ -679,6 +725,8 @@ GEM
       activesupport (>= 3.1, < 4.3)
     select2-rails (3.5.9.3)
       thor (~> 0.14)
+    sentry-raven (0.15.3)
+      faraday (>= 0.7.6)
     settingslogic (2.0.9)
     sexp_processor (4.6.0)
     sham_rack (1.3.6)
@@ -773,7 +821,6 @@ GEM
       multi_json (~> 1.7)
       twitter-stream (~> 0.1)
     tins (1.6.0)
-    trollop (2.1.2)
     turbolinks (2.5.3)
       coffee-rails
     twitter-stream (0.1.16)
@@ -822,6 +869,7 @@ GEM
       builder
       expression_parser
       rinku
+    xml-simple (1.1.5)
     xpath (2.0.0)
       nokogiri (~> 1.3)
 
@@ -865,7 +913,7 @@ DEPENDENCIES
   d3_rails (~> 3.5.0)
   database_cleaner (~> 1.4.0)
   default_value_for (~> 3.0.0)
-  devise (~> 3.5.3)
+  devise (~> 3.5.4)
   devise-async (~> 0.9.0)
   devise-two-factor (~> 2.0.0)
   diffy (~> 3.0.3)
@@ -877,7 +925,7 @@ DEPENDENCIES
   ffaker (~> 2.0.0)
   flay
   flog
-  fog (~> 1.25.0)
+  fog (~> 1.36.0)
   font-awesome-rails (~> 4.2)
   foreman
   fuubar (~> 2.0.0)
@@ -886,7 +934,7 @@ DEPENDENCIES
   github-markup (~> 1.3.1)
   gitlab-flowdock-git-hook (~> 1.0.1)
   gitlab_emoji (~> 0.2.0)
-  gitlab_git (~> 7.2.22)
+  gitlab_git (~> 7.2.23)
   gitlab_meta (= 7.0)
   gitlab_omniauth-ldap (~> 1.2.1)
   gollum-lib (~> 4.1.0)
@@ -962,6 +1010,7 @@ DEPENDENCIES
   sdoc (~> 0.3.20)
   seed-fu (~> 2.3.5)
   select2-rails (~> 3.5.9)
+  sentry-raven
   settingslogic (~> 2.0.9)
   sham_rack
   shoulda-matchers (~> 2.8.0)
diff --git a/Procfile b/Procfile
index 9cfdee7040fb72406291e7c8e9cd0d180f7f2456..cad738d4292169d2eed99e2e598c56f7125bb7d3 100644
--- a/Procfile
+++ b/Procfile
@@ -2,6 +2,6 @@
 # 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 mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default
+web: RAILS_ENV=development bin/web start_foreground
+worker: RAILS_ENV=development bin/background_jobs start_foreground
 # mail_room: bundle exec mail_room -q -c config/mail_room.yml
diff --git a/Rakefile b/Rakefile
old mode 100644
new mode 100755
diff --git a/VERSION b/VERSION
index ce669730119df83f8d693ec9e7401baf55b9245a..ffec98087cb4de19d42eddbba9f1316d755937e9 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-8.4.0.pre
+8.5.0-pre
diff --git a/app/assets/fonts/OFL.txt b/app/assets/fonts/OFL.txt
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf.woff b/app/assets/fonts/SourceSansPro-Black.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Black.ttf.woff2 b/app/assets/fonts/SourceSansPro-Black.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..c90d078406c7ab07d454a933c76b61f62daf10b1
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-Black.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff b/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..b87e22c41b5ef4fca17e91da8e40996b60cf25c9
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BlackIt.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf.woff b/app/assets/fonts/SourceSansPro-Bold.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Bold.ttf.woff2 b/app/assets/fonts/SourceSansPro-Bold.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..0f46f3e833af794f142f15af5dc4475c526b8d92
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-Bold.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff b/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..8007df6df327f2d025a2f8334ac7475b33592a48
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-BoldIt.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff b/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2 b/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..b715f27408281e692bf11516adcff6073eb090a9
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-ExtraLight.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..d8f9d29d4aa2d56dc081c82a69bb7c82040a4744
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-ExtraLightIt.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-It.ttf.woff b/app/assets/fonts/SourceSansPro-It.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-It.ttf.woff2 b/app/assets/fonts/SourceSansPro-It.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..a00852641f807544be66f86b82a7cd9fe9019683
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-It.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-Light.ttf.woff b/app/assets/fonts/SourceSansPro-Light.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Light.ttf.woff2 b/app/assets/fonts/SourceSansPro-Light.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..d8b610ad76eee1b15ef10d6f3593a75e732cc975
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-Light.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf.woff b/app/assets/fonts/SourceSansPro-LightIt.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..e0eebac8273f1c172e550b1e9e3b50cd6c49fdd8
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-LightIt.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf.woff b/app/assets/fonts/SourceSansPro-Regular.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Regular.ttf.woff2 b/app/assets/fonts/SourceSansPro-Regular.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..0dd3464c74be96d2285b3910222aec5750cc57fd
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-Regular.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf.woff b/app/assets/fonts/SourceSansPro-Semibold.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2 b/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..2526d2e1b602bc40c347df3f78ce757a9b5adc0a
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-Semibold.ttf.woff2 differ
diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff
old mode 100755
new mode 100644
diff --git a/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2 b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..606935af0897f35259bb02ab8258e5de1e5525d3
Binary files /dev/null and b/app/assets/fonts/SourceSansPro-SemiboldIt.ttf.woff2 differ
diff --git a/app/assets/javascripts/activities.js.coffee b/app/assets/javascripts/activities.js.coffee
index 63803747413444097550de267b493ccbd930890d..3b6b453ac51b4206457aaacb417d02dde194c2be 100644
--- a/app/assets/javascripts/activities.js.coffee
+++ b/app/assets/javascripts/activities.js.coffee
@@ -1,7 +1,7 @@
 class @Activities
   constructor: ->
     Pager.init 20, true
-    $(".event-filter .btn").bind "click", (event) =>
+    $(".event-filter a").bind "click", (event) =>
       event.preventDefault()
       @toggleFilter($(event.currentTarget))
       @reloadActivities()
@@ -12,7 +12,7 @@ class @Activities
 
 
   toggleFilter: (sender) ->
-    sender.toggleClass "active"
+    sender.closest('li').toggleClass "active"
     event_filters = $.cookie("event_filter")
     filter = sender.attr("id").split("_")[0]
     if event_filters
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee
index bcb2e6df7c01450cc9bccd1a530f8811d2ae6976..eb951f717112e3160a876a0385a160832231b0ee 100644
--- a/app/assets/javascripts/admin.js.coffee
+++ b/app/assets/javascripts/admin.js.coffee
@@ -10,19 +10,19 @@ class @Admin
 
     $('body').on 'click', '.js-toggle-colors-link', (e) ->
       e.preventDefault()
-      $('.js-toggle-colors-link').hide()
-      $('.js-toggle-colors-container').show()
+      $('.js-toggle-colors-container').toggle()
 
     $('input#broadcast_message_color').on 'input', ->
-      previewColor = $('input#broadcast_message_color').val()
+      previewColor = $(@).val()
       $('div.broadcast-message-preview').css('background-color', previewColor)
 
     $('input#broadcast_message_font').on 'input', ->
-      previewColor = $('input#broadcast_message_font').val()
+      previewColor = $(@).val()
       $('div.broadcast-message-preview').css('color', previewColor)
 
     $('textarea#broadcast_message_message').on 'input', ->
-      previewMessage = $('textarea#broadcast_message_message').val()
+      previewMessage = $(@).val()
+      previewMessage = "Your message here" if previewMessage.trim() == ''
       $('div.broadcast-message-preview span').text(previewMessage)
 
     $('.log-tabs a').click (e) ->
diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee
index 619abb1fb071367785e20e80b975d8c4199a8af4..1ef31c7700e33221f0fc89e210ef0c75e63402e1 100644
--- a/app/assets/javascripts/awards_handler.coffee
+++ b/app/assets/javascripts/awards_handler.coffee
@@ -5,7 +5,7 @@ class @AwardsHandler
       event.preventDefault()
       $(".emoji-menu").show()
 
-    $("html").click ->
+    $("html").on 'click', (event) ->
       if !$(event.target).closest(".emoji-menu").length
         if $(".emoji-menu").is(":visible")
           $(".emoji-menu").hide()
@@ -19,7 +19,7 @@ class @AwardsHandler
       @addAwardToEmojiBar(emoji)
 
     $(".emoji-menu").hide()
-    
+
   addAwardToEmojiBar: (emoji) ->
     @addEmojiToFrequentlyUsedList(emoji)
 
@@ -44,7 +44,6 @@ class @AwardsHandler
   decrementCounter: (emoji) ->
     counter = @findEmojiIcon(emoji).siblings(".counter")
     emojiIcon = counter.parent()
-
     if parseInt(counter.text()) > 1
       counter.text(parseInt(counter.text()) - 1)
       emojiIcon.removeClass("active")
@@ -60,13 +59,16 @@ class @AwardsHandler
   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)
+    authors.splice(authors.indexOf("me"),1)
+    award_block.closest(".award").attr("data-original-title", authors.join(", "))
     @resetTooltip(award_block)
 
   addMeToAuthorList: (emoji) ->
     award_block = @findEmojiIcon(emoji).parent()
-    authors = award_block.attr("data-original-title").split(", ")
+    origTitle = award_block.attr("data-original-title").trim()
+    authors = []
+    if origTitle
+      authors = origTitle.split(', ')
     authors.push("me")
     award_block.attr("title", authors.join(", "))
     @resetTooltip(award_block)
@@ -78,7 +80,7 @@ class @AwardsHandler
     setTimeout (->
       award.tooltip()
     ), 200
-    
+
 
   createEmoji: (emoji) ->
     emojiCssClass = @resolveNameToCssClass(emoji)
diff --git a/app/assets/javascripts/behaviors/autosize.js.coffee b/app/assets/javascripts/behaviors/autosize.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..b32072e61ee6af8449be753b88f60e034a214bf1
--- /dev/null
+++ b/app/assets/javascripts/behaviors/autosize.js.coffee
@@ -0,0 +1,4 @@
+#= require autosize
+
+$ ->
+  autosize($('.js-autosize'))
diff --git a/app/assets/javascripts/build_artifacts.js.coffee b/app/assets/javascripts/build_artifacts.js.coffee
new file mode 100644
index 0000000000000000000000000000000000000000..5ae6cba56c8ee020e69da29806799b3b25569482
--- /dev/null
+++ b/app/assets/javascripts/build_artifacts.js.coffee
@@ -0,0 +1,14 @@
+class @BuildArtifacts
+  constructor: () ->
+    @disablePropagation()
+    @setupEntryClick()
+
+  disablePropagation: ->
+    $('.top-block').on 'click', '.download',  (e) ->
+      e.stopPropagation()
+    $('.tree-holder').on 'click', 'tr[data-link] a', (e) ->
+      e.stopImmediatePropagation()
+
+  setupEntryClick: ->
+    $('.tree-holder').on 'click', 'tr[data-link]', (e) ->
+      window.location = @dataset.link
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index 58d6b9d406035154950868e9e73621cfe4b68b56..2cdf01d874cda3e2632d6ff0372dcf3d45a67947 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -87,7 +87,6 @@ class Dispatcher
         new GroupAvatar()
       when 'projects:tree:show'
         new TreeView()
-        shortcut_handler = new ShortcutsTree()
       when 'projects:find_file:show'
         shortcut_handler = true
       when 'projects:blob:show'
@@ -101,6 +100,8 @@ class Dispatcher
         shortcut_handler = true
       when 'projects:forks:new'
         new ProjectFork()
+      when 'projects:artifacts:browse'
+        new BuildArtifacts()
       when 'users:show'
         new User()
         new Activities()
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index c256ec8f41bb178189c5e39ce1a589d312a05dd8..cbc70cd846cfb50f2128f2fd78d5af9d57bb3531 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -6,22 +6,40 @@ class @Issue
   constructor: ->
     # Prevent duplicate event bindings
     @disableTaskList()
-
+    @fixAffixScroll()
     if $('a.btn-close').length
       @initTaskList()
       @initIssueBtnEventListeners()
 
+  fixAffixScroll: ->
+    fixAffix = ->
+      $discussion = $('.issuable-discussion')
+      $sidebar = $('.issuable-sidebar')
+      if $sidebar.hasClass('no-affix')
+        $sidebar.removeClass(['affix-top','affix'])
+      discussionHeight = $discussion.height()
+      sidebarHeight = $sidebar.height()
+      if sidebarHeight > discussionHeight
+        $discussion.height(sidebarHeight + 50)
+        $sidebar.addClass('no-affix')
+    $(window).on('resize', fixAffix)
+    fixAffix()
+
   initTaskList: ->
     $('.detail-page-description .js-task-list-container').taskList('enable')
     $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
 
   initIssueBtnEventListeners: ->
+    _this = @
     issueFailMessage = 'Unable to update this issue at this time.'
     $('a.btn-close, a.btn-reopen').on 'click', (e) ->
       e.preventDefault()
       e.stopImmediatePropagation()
       $this = $(this)
       isClose = $this.hasClass('btn-close')
+      shouldSubmit = $this.hasClass('btn-comment')
+      if shouldSubmit
+        _this.submitNoteForm($this.closest('form'))
       $this.prop('disabled', true)
       url = $this.attr('href')
       $.ajax
@@ -32,12 +50,13 @@ class @Issue
           new Flash(issueFailMessage, 'alert')
         success: (data, textStatus, jqXHR) ->
           if data.saved
-            $this.addClass('hidden')
             if isClose
+              $('a.btn-close').addClass('hidden')
               $('a.btn-reopen').removeClass('hidden')
               $('div.status-box-closed').removeClass('hidden')
               $('div.status-box-open').addClass('hidden')
             else
+              $('a.btn-reopen').addClass('hidden')
               $('a.btn-close').removeClass('hidden')
               $('div.status-box-closed').addClass('hidden')
               $('div.status-box-open').removeClass('hidden')
@@ -45,6 +64,11 @@ class @Issue
             new Flash(issueFailMessage, 'alert')
           $this.prop('disabled', false)
 
+  submitNoteForm: (form) =>
+    noteText = form.find("textarea.js-note-text").val()
+    if noteText.trim().length > 0
+      form.submit()
+
   disableTaskList: ->
     $('.detail-page-description .js-task-list-container').taskList('disable')
     $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
diff --git a/app/assets/javascripts/logo.js.coffee b/app/assets/javascripts/logo.js.coffee
index e864a674cdd70ba615327f1968702b0b522fcbb4..a5879c8b7934d6a69e8fa0bf4e7e79e8b3a76c15 100644
--- a/app/assets/javascripts/logo.js.coffee
+++ b/app/assets/javascripts/logo.js.coffee
@@ -21,6 +21,7 @@ start = ->
   clearHighlights()
   pieceIndex = 0
   pieces.reverse() unless pieces[0] == firstPiece
+  clearInterval(currentTimer) if currentTimer
   currentTimer = setInterval(work, delay)
 
 stop = ->
diff --git a/app/assets/javascripts/merge_request.js.coffee b/app/assets/javascripts/merge_request.js.coffee
index 9047587db81af96a3aeca6a6451b64851a3cd4ba..6af5a48a0bbe87be5038971858df4039ce97f2f0 100644
--- a/app/assets/javascripts/merge_request.js.coffee
+++ b/app/assets/javascripts/merge_request.js.coffee
@@ -15,10 +15,13 @@ class @MergeRequest
     this.$('.show-all-commits').on 'click', =>
       this.showAllCommits()
 
+    @fixAffixScroll();
+
     @initTabs()
 
     # Prevent duplicate event bindings
     @disableTaskList()
+    @initMRBtnListeners()
 
     if $("a.btn-close").length
       @initTaskList()
@@ -27,6 +30,20 @@ class @MergeRequest
   $: (selector) ->
     this.$el.find(selector)
 
+  fixAffixScroll: ->
+    fixAffix = ->
+      $discussion = $('.issuable-discussion')
+      $sidebar = $('.issuable-sidebar')
+      if $sidebar.hasClass('no-affix')
+        $sidebar.removeClass(['affix-top','affix'])
+      discussionHeight = $discussion.height()
+      sidebarHeight = $sidebar.height()
+      if sidebarHeight > discussionHeight
+        $discussion.height(sidebarHeight + 50)
+        $sidebar.addClass('no-affix')
+    $(window).on('resize', fixAffix)
+    fixAffix()
+
   initTabs: ->
     if @opts.action != 'new'
       # `MergeRequests#new` has no tab-persisting or lazy-loading behavior
@@ -43,6 +60,28 @@ class @MergeRequest
     $('.detail-page-description .js-task-list-container').taskList('enable')
     $(document).on 'tasklist:changed', '.detail-page-description .js-task-list-container', @updateTaskList
 
+  initMRBtnListeners: ->
+    _this = @
+    $('a.btn-close, a.btn-reopen').on 'click', (e) ->
+      $this = $(this)
+      shouldSubmit = $this.hasClass('btn-comment')
+      if shouldSubmit && $this.data('submitted')
+        return
+      if shouldSubmit
+        if $this.hasClass('btn-comment-and-close') || $this.hasClass('btn-comment-and-reopen')
+          e.preventDefault()
+          e.stopImmediatePropagation()
+          _this.submitNoteForm($this.closest('form'),$this)
+
+
+  submitNoteForm: (form, $button) =>
+    noteText = form.find("textarea.js-note-text").val()
+    if noteText.trim().length > 0
+      form.submit()
+      $button.data('submitted',true)
+      $button.trigger('click')
+
+
   disableTaskList: ->
     $('.detail-page-description .js-task-list-container').taskList('disable')
     $(document).off 'tasklist:changed', '.detail-page-description .js-task-list-container'
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 9e2dc1250c9fa556029b9fbf1e6c7f0dc94d13ee..b10e1db7f3fc895aa6a8c0e4c030009506f86ffa 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -5,7 +5,7 @@
 #
 # ### Example Markup
 #
-#   <ul class="nav nav-tabs merge-request-tabs">
+#   <ul class="nav-links merge-request-tabs">
 #     <li class="notes-tab active">
 #       <a data-action="notes" data-target="#notes" data-toggle="tab" href="/foo/bar/merge_requests/1">
 #         Discussion
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 9e5204bfeebb7b474ad935e64a3eb59e46d2daa2..2bfc5cb2d9c556de76b431060377b3e810013d86 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -1,4 +1,5 @@
 #= require autosave
+#= require autosize
 #= require dropzone
 #= require dropzone_input
 #= require gfm_auto_complete
@@ -33,8 +34,6 @@ class @Notes
     $(document).on "click", ".note-edit-cancel", @cancelEdit
 
     # Reopen and close actions for Issue/MR combined with note form submit
-    $(document).on "click", ".js-note-target-reopen", @targetReopen
-    $(document).on "click", ".js-note-target-close", @targetClose
     $(document).on "click", ".js-comment-button", @updateCloseButton
     $(document).on "keyup", ".js-note-text", @updateTargetButtons
 
@@ -248,6 +247,7 @@ class @Notes
       else
         previewButton.removeClass("turn-on").addClass "turn-off"
 
+    autosize(textarea)
     new Autosave textarea, [
       "Note"
       form.find("#note_commit_id").val()
@@ -320,6 +320,7 @@ class @Notes
     form.show()
     textarea = form.find("textarea")
     textarea.focus()
+    autosize(textarea)
 
     # HACK (rspeicher/DouweM): Work around a Chrome 43 bug(?).
     # The textarea has the correct value, Chrome just won't show it unless we
@@ -355,7 +356,7 @@ class @Notes
     $('.note[id="' + note_id + '"]').each ->
       note = $(this)
       notes = note.closest(".notes")
-      count = notes.closest(".notes_holder").find(".discussion-notes-count")
+      count = notes.closest(".issuable-details").find(".notes-tab .badge")
 
       # check if this is the last note for this line
       if notes.find(".note").length is 1
@@ -365,9 +366,10 @@ class @Notes
 
         # for diff lines
         notes.closest("tr").remove()
-      else
-        # update notes count
-        count.get(0).lastChild.nodeValue = " #{notes.children().length - 1}"
+
+      # update notes count
+      oldNum = parseInt(count.text())
+      count.text(oldNum - 1)
 
       note.remove()
 
@@ -512,17 +514,6 @@ class @Notes
   visibilityChange: =>
     @refresh()
 
-  targetReopen: (e) =>
-    @submitNoteForm($(e.target).parents('form'))
-
-  targetClose: (e) =>
-    @submitNoteForm($(e.target).parents('form'))
-
-  submitNoteForm: (form) =>
-    noteText = form.find(".js-note-text").val()
-    if noteText.trim().length > 0
-      form.submit()
-
   updateCloseButton: (e) =>
     textarea = $(e.target)
     form = textarea.parents('form')
@@ -531,13 +522,16 @@ class @Notes
   updateTargetButtons: (e) =>
     textarea = $(e.target)
     form = textarea.parents('form')
-
     if textarea.val().trim().length > 0
       form.find('.js-note-target-reopen').text('Comment & reopen')
       form.find('.js-note-target-close').text('Comment & close')
+      form.find('.js-note-target-reopen').addClass('btn-comment-and-reopen')
+      form.find('.js-note-target-close').addClass('btn-comment-and-close')
     else
       form.find('.js-note-target-reopen').text('Reopen')
       form.find('.js-note-target-close').text('Close')
+      form.find('.js-note-target-reopen').removeClass('btn-comment-and-reopen')
+      form.find('.js-note-target-close').removeClass('btn-comment-and-close')
 
   initTaskList: ->
     @enableTaskList()
diff --git a/app/assets/javascripts/shortcuts.js.coffee b/app/assets/javascripts/shortcuts.js.coffee
index 4d915bfc8c5c3e83b8d2bfd94659050ed2d0edcd..f141fb69c3ee474dbb13b7c15e1f56d8a9c1e51f 100644
--- a/app/assets/javascripts/shortcuts.js.coffee
+++ b/app/assets/javascripts/shortcuts.js.coffee
@@ -4,6 +4,7 @@ class @Shortcuts
     Mousetrap.reset()
     Mousetrap.bind('?', @selectiveHelp)
     Mousetrap.bind('s', Shortcuts.focusSearch)
+    Mousetrap.bind('t', -> Turbolinks.visit(findFileURL)) if findFileURL?
 
   selectiveHelp: (e) =>
     Shortcuts.showHelp(e, @enabledHelp)
diff --git a/app/assets/javascripts/shortcuts_tree.coffee b/app/assets/javascripts/shortcuts_tree.coffee
deleted file mode 100644
index ba0839c9fc005ea96cc68acf9ff7069ead5d425b..0000000000000000000000000000000000000000
--- a/app/assets/javascripts/shortcuts_tree.coffee
+++ /dev/null
@@ -1,4 +0,0 @@
-class @ShortcutsTree extends ShortcutsNavigation
-  constructor: ->
-    super()
-    Mousetrap.bind('t', -> ShortcutsTree.findAndFollowLink('.shortcuts-find-file'))
diff --git a/app/assets/javascripts/star.js.coffee b/app/assets/javascripts/star.js.coffee
index d849b2e79500adbf730f3bee7a2ab1fb0ec18527..f27780dda931d712facd77389375280e2f761e2b 100644
--- a/app/assets/javascripts/star.js.coffee
+++ b/app/assets/javascripts/star.js.coffee
@@ -6,7 +6,7 @@ class @Star
       $starIcon = $this.find('i')
 
       toggleStar = (isStarred) ->
-        $this.parent().find('span.count').text data.star_count
+        $this.parent().find('.star-count').text data.star_count
         if isStarred
           $starSpan.removeClass('starred').text 'Star'
           $starIcon.removeClass('fa-star').addClass 'fa-star-o'
@@ -19,4 +19,4 @@ class @Star
       return
     ).on 'ajax:error', (e, xhr, status, error) ->
       new Flash('Star toggle failed. Try again later.', 'alert')
-      return
\ No newline at end of file
+      return
diff --git a/app/assets/javascripts/wikis.js.coffee b/app/assets/javascripts/wikis.js.coffee
index 81cfc37b956f5ef458d6fc849db1d5206fd4fa2c..19420f42468fe5e6ace7b543aa1ed43644990960 100644
--- a/app/assets/javascripts/wikis.js.coffee
+++ b/app/assets/javascripts/wikis.js.coffee
@@ -1,17 +1,18 @@
+#= require latinise
+
 class @Wikis
   constructor: ->
-    $('.build-new-wiki').bind "click", (e) ->
-      $('[data-error~=slug]').addClass("hidden")
-      $('p.hint').show()
+    $('.build-new-wiki').bind 'click', (e) =>
+      $('[data-error~=slug]').addClass('hidden')
       field = $('#new_wiki_path')
-      valid_slug_pattern = /^[\w\/-]+$/
+      slug = @slugify(field.val())
 
-      slug = field.val()
-      if slug.match valid_slug_pattern
+      if (slug.length > 0)
         path = field.attr('data-wikis-path')
-        if(slug.length > 0)
-          location.href = path + "/" + slug
-      else
-        e.preventDefault()
-        $('p.hint').hide()
-        $('[data-error~=slug]').removeClass("hidden")
+        location.href = path + '/' + slug
+
+  dasherize: (value) ->
+    value.replace(/[_\s]+/g, '-')
+
+  slugify: (value) =>
+    @dasherize(value.trim().toLowerCase().latinise())
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 48a4971c8fca834044693063d2ac031688122d0e..fa7641b1676d236fc9b1b45630b7c5653f26c027 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -24,6 +24,7 @@
 @import "framework/lists.scss";
 @import "framework/markdown_area.scss";
 @import "framework/mobile.scss";
+@import "framework/nav.scss";
 @import "framework/pagination.scss";
 @import "framework/panels.scss";
 @import "framework/selects.scss";
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index fa0e70847f34d86e7ddcec02243b31d8090b80aa..d0f5d33bf4d0a6f344c6efda9993cba5a1d94e80 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -18,9 +18,9 @@
   line-height: 36px;
 }
 
-.content-block,
 .gray-content-block {
-  margin: -$gl-padding;
+  margin-top: 0;
+  margin-bottom: -$gl-padding;
   background-color: $background-color;
   padding: $gl-padding;
   margin-bottom: 0px;
@@ -86,10 +86,7 @@
 .cover-block {
   text-align: center;
   background: $background-color;
-  margin: -$gl-padding;
-  margin-bottom: 0;
-  padding: 44px $gl-padding;
-  border-bottom: 1px solid $border-color;
+  padding-top: 44px;
   position: relative;
 
   .avatar-holder {
@@ -136,3 +133,19 @@
 .block-connector {
   margin-top: -1px;
 }
+
+.nav-block {
+  .controls {
+    float: right;
+    margin-top: 11px;
+  }
+}
+
+.content-block {
+  padding: $gl-padding 0;
+  border-bottom: 1px solid $border-color;
+
+  &.oneline-block {
+    line-height: 42px;
+  }
+}
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 97a94638847576c5b8695581e3dd83f21c781865..c99292c3f83ecdba33cc3c407be68a90b6e9c4f0 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -1,12 +1,8 @@
 @mixin btn-default {
   @include border-radius(3px);
-  border-width: 1px;
-  border-style: solid;
-  font-size: 15px;
+  font-size: $gl-font-size;
   font-weight: 500;
-  line-height: 18px;
-  padding: 11px $gl-padding;
-  letter-spacing: .4px;
+  padding: $gl-vert-padding $gl-padding;
 
   &:focus,
   &:active {
@@ -17,8 +13,6 @@
 
 @mixin btn-middle {
   @include btn-default;
-  @include border-radius(3px);
-  padding: 11px 24px;
 }
 
 @mixin btn-color($light, $border-light, $normal, $border-normal, $dark, $border-dark, $color) {
@@ -74,16 +68,15 @@
   @include btn-default;
   @include btn-white;
 
+  &.btn-small,
   &.btn-sm {
-    padding: 5px 10px;
-  }
-
-  &.btn-nr {
-    padding: 7px 10px;
+    padding: 4px 10px;
+    font-size: 13px;
+    line-height: 18px;
   }
 
   &.btn-xs {
-    padding: 1px 5px;
+    padding: 2px 5px;
   }
 
   &.btn-success,
@@ -131,6 +124,12 @@
     &:last-child {
       margin-right: 0px;
     }
+    &.btn-xs {
+      margin-right: 3px;
+    }
+  }
+  &.disabled {
+    pointer-events: auto !important;
   }
 }
 
@@ -153,33 +152,42 @@
   }
 }
 
-.btn-group-next {
+.btn-clipboard {
+  border: none;
+  padding: 0 5px;
+}
+
+.input-group-btn {
   .btn {
-    padding: 9px 0px;
-    font-size: 15px;
-    color: #7f8fa4;
-    border-color: #e7e9ed;
-    width: 140px;
-
-    .badge {
-      font-weight: normal;
-      background-color: #eee;
-      color: #78a;
+    @include btn-gray;
+    @include btn-middle;
+
+    &:hover {
+      outline: none;
     }
 
-    &.active {
-      border-color: $gl-info;
-      background: $gl-info;
-      color: #fff;
+    &:focus {
+      outline: none;
+    }
+
+    &:active {
+      outline: none;
+    }
 
-      .badge {
-        color: $gl-info;
-        background-color: white;
-      }
+    &.btn-clipboard {
+      padding-left: 15px;
+      padding-right: 15px;
     }
   }
-}
 
-.btn-clipboard {
-  border: none;
+  .active {
+    @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
+
+    border: 1px solid #c6cacf !important;
+    background-color: #e4e7ed !important;
+  }
+
+  .btn-green {
+    @include btn-green
+  }
 }
diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss
index 11730000f85e32f06b75fe0c282f7c3e0e00d802..585a9d8391336afeb14b3d0e7f31444b88b2effe 100644
--- a/app/assets/stylesheets/framework/common.scss
+++ b/app/assets/stylesheets/framework/common.scss
@@ -75,6 +75,8 @@ hr {
   @include str-truncated;
 }
 
+.item-title { font-weight: 600; }
+
 /** FLASH message **/
 .author_link {
   color: $gl-link-color;
@@ -374,75 +376,6 @@ table {
   }
 }
 
-.center-top-menu, .left-top-menu {
-  @include nav-menu;
-  text-align: center;
-  margin-top: 5px;
-  margin-bottom: $gl-padding;
-  height: auto;
-  margin-top: -$gl-padding;
-
-  &.no-bottom {
-    margin-bottom: 0;
-  }
-
-  &.no-top {
-    margin-top: 0;
-  }
-
-  li a {
-    display: inline-block;
-    padding-top: $gl-padding;
-    padding-bottom: 11px;
-    margin-bottom: -1px;
-  }
-
-  &.bottom-border {
-    border-bottom: 1px solid $border-color;
-    height: 57px;
-  }
-
-  &.wide {
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
-  }
-}
-
-.left-top-menu {
-  text-align: left;
-  border-bottom: 1px solid #EEE;
-}
-
-.center-middle-menu {
-  @include nav-menu;
-  padding: 0;
-  text-align: center;
-  margin: -$gl-padding;
-  margin-top: 0;
-  margin-bottom: 0;
-  height: 58px;
-  border-bottom: 1px solid $border-color;
-
-  li {
-    &:after {
-      content: "|";
-      color: $border-gray-light;
-    }
-
-    &:last-child {
-      &:after {
-        content: none;
-      }
-    }
-
-    > a {
-      display: inline-block;
-      text-transform: uppercase;
-      font-size: 13px;
-    }
-  }
-}
-
 .dropzone .dz-preview .dz-progress {
   border-color: $border-color !important;
 }
diff --git a/app/assets/stylesheets/framework/files.scss b/app/assets/stylesheets/framework/files.scss
index cbfd4bc29b636fdde456116b7f970c0aea75e7d2..6ee104ee31a575731680d569c7fcd38d22d6a5be 100644
--- a/app/assets/stylesheets/framework/files.scss
+++ b/app/assets/stylesheets/framework/files.scss
@@ -3,11 +3,8 @@
  *
  */
 .file-holder {
-  margin-left: -$gl-padding;
-  margin-right: -$gl-padding;
   border: none;
-  border-top: 1px solid #E7E9EE;
-  border-bottom: 1px solid #E7E9EE;
+  border: 1px solid $border-color;
 
   &.readme-holder {
     border-bottom: 0;
diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss
index 8e6922c9231149bf39cfa2d7a1849230f3f3cd36..b7638c86bfa12f5f0346205e5612f1cdb820a67b 100644
--- a/app/assets/stylesheets/framework/filters.scss
+++ b/app/assets/stylesheets/framework/filters.scss
@@ -1,5 +1,5 @@
 .filter-item {
-  margin-right: 15px;
+  margin-right: 6px;
 }
 
 @media (min-width: 800px)  {
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index 82eb50ad4be068473bac0507f6fc1a1aa75750b0..1bfd0213995c45b4bdbd034803eb41b7dbc928bc 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -8,10 +8,12 @@
   .flash-notice {
     @extend .alert;
     @extend .alert-info;
+    margin: 0;
   }
 
   .flash-alert {
     @extend .alert;
     @extend .alert-danger;
+    margin: 0;
   }
 }
diff --git a/app/assets/stylesheets/framework/fonts.scss b/app/assets/stylesheets/framework/fonts.scss
index 20988f7b4300ac3a68c4f38a3a6d0ebcf5caee60..7a946109e3af43f990ba210db98e5cfb814036af 100644
--- a/app/assets/stylesheets/framework/fonts.scss
+++ b/app/assets/stylesheets/framework/fonts.scss
@@ -3,23 +3,39 @@
   font-family: 'Source Sans Pro';
   font-style: normal;
   font-weight: 300;
-  src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), font-url('SourceSansPro-Light.ttf.woff');
+  src:
+    local('Source Sans Pro Light'),
+    local('SourceSansPro-Light'),
+    font-url('SourceSansPro-Light.ttf.woff2') format('woff2'),
+    font-url('SourceSansPro-Light.ttf.woff') format('woff');
 }
 @font-face {
   font-family: 'Source Sans Pro';
   font-style: normal;
   font-weight: 400;
-  src: local('Source Sans Pro'), local('SourceSansPro-Regular'), font-url('SourceSansPro-Regular.ttf.woff');
+  src:
+    local('Source Sans Pro'),
+    local('SourceSansPro-Regular'),
+    font-url('SourceSansPro-Regular.ttf.woff2') format('woff2'),
+    font-url('SourceSansPro-Regular.ttf.woff') format('woff');
 }
 @font-face {
   font-family: 'Source Sans Pro';
   font-style: normal;
   font-weight: 600;
-  src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), font-url('SourceSansPro-Semibold.ttf.woff');
+  src:
+    local('Source Sans Pro Semibold'),
+    local('SourceSansPro-Semibold'),
+    font-url('SourceSansPro-Semibold.ttf.woff2') format('woff2'),
+    font-url('SourceSansPro-Semibold.ttf.woff') format('woff');
 }
 @font-face {
   font-family: 'Source Sans Pro';
   font-style: normal;
   font-weight: 700;
-  src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), font-url('SourceSansPro-Bold.ttf.woff');
+  src:
+    local('Source Sans Pro Bold'),
+    local('SourceSansPro-Bold'),
+    font-url('SourceSansPro-Bold.ttf.woff2') format('woff2'),
+    font-url('SourceSansPro-Bold.ttf.woff') format('woff');
 }
diff --git a/app/assets/stylesheets/framework/forms.scss b/app/assets/stylesheets/framework/forms.scss
index 032d343df44e3060ec5fc66499904856839b841d..4dab806d50e4338dd581d843df6f0255c38add79 100644
--- a/app/assets/stylesheets/framework/forms.scss
+++ b/app/assets/stylesheets/framework/forms.scss
@@ -74,8 +74,10 @@ label {
 
 .form-control {
   @include box-shadow(none);
-  height: 42px;
-  padding: 8px $gl-padding;
+}
+
+.form-control-inline {
+  display: inline;
 }
 
 .wiki-content {
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index 4dbbb56104bdca5b3dd1d411f449b9ecbc4cca8d..ba5e72c8c5a45357ced756972600ad90764535fc 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -28,6 +28,7 @@ header {
     min-height: $header-height;
     background-color: #fff;
     border: none;
+    border-bottom: 1px solid #EEE;
 
     .container-fluid {
       width: 100% !important;
diff --git a/app/assets/stylesheets/framework/jquery.scss b/app/assets/stylesheets/framework/jquery.scss
index 871b808bad4c4aca098f8aacfae64d3160dfba1f..d6cd78813c0aa164a41f9ee92b0b305ef7fbc5f2 100644
--- a/app/assets/stylesheets/framework/jquery.scss
+++ b/app/assets/stylesheets/framework/jquery.scss
@@ -53,3 +53,14 @@
     color: #333;
   }
 }
+
+.ui-sortable-handle {
+  cursor: move;
+  cursor: -webkit-grab;
+  cursor: -moz-grab;
+
+  &:active {
+    cursor: -webkit-grabbing;
+    cursor: -moz-grabbing;
+  }
+}
diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss
index a1a9990241d6964d96bda9f13e882c2c7ae9c66e..e901c78d02fb06ee9b87b78771be024bc66a2b87 100644
--- a/app/assets/stylesheets/framework/layout.scss
+++ b/app/assets/stylesheets/framework/layout.scss
@@ -5,8 +5,6 @@ html {
 }
 
 body {
-  background-color: #F3F3F3 !important;
-
   &.navless {
     background-color: white !important;
   }
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index 1c74e525a608348f04250da3177c9fb01ce15838..c6bc6fb324d5707a135c3666e638c57b0e8c09ed 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -74,7 +74,7 @@
 
 
 /** light list with border-bottom between li **/
-ul.bordered-list {
+ul.bordered-list, ul.unstyled-list {
   @include basic-list;
 
   &.top-list {
@@ -88,6 +88,10 @@ ul.bordered-list {
   }
 }
 
+ul.unstyled-list > li {
+  border-bottom: none;
+}
+
 ul.task-list {
   li.task-list-item {
     list-style-type: none;
@@ -105,10 +109,8 @@ ul.content-list {
   padding: 0;
 
   > li {
-    padding: $gl-padding;
+    padding: $gl-padding 0;
     border-color: $table-border-color;
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
     color: $gl-gray;
 
     .avatar {
@@ -129,6 +131,7 @@ ul.content-list {
 .panel > .content-list {
   li {
     margin: 0;
+    padding: $gl-padding;
   }
 }
 
@@ -144,7 +147,7 @@ ul.controls {
   > li {
     float: left;
     margin-right: 10px;
-    
+
     &:last-child {
       margin-right: 0;
     }
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index 4a00a197d9a0c817eaa19345e81fe6b176234e46..6732343802a5b4f4693270e75068a4dc23a201c3 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -65,13 +65,6 @@
   position: relative;
 }
 
-.md-header {
-  ul {
-    float: left;
-    margin-bottom: 1px;
-  }
-}
-
 .referenced-users {
   color: #4c4e54;
   padding-top: 10px;
@@ -85,28 +78,12 @@
   box-shadow: none;
 }
 
-.new_note,
-.edit_note,
-.detail-page-description,
-.milestone-description,
-.wiki-content,
-.merge-request-form {
-  .nav-tabs {
-    margin-bottom: 0;
-    border: none;
-
-    li a,
-    li.active a {
-      border: 1px solid #DDD;
-    }
-  }
-}
-
 .markdown-area {
   @include border-radius(0);
   background: #FFF;
   border: 1px solid #ddd;
   min-height: 140px;
+  max-height: 430px;
   padding: 5px;
   box-shadow: none;
   width: 100%;
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 41fd890f14f8046b858b3e6f56fffb4dd16ba338..1d5000fe38881cdf5b388ab0ebf35caea083e2f8 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -118,38 +118,3 @@
   font-size: 16px;
   line-height: 24px;
 }
-
-@mixin nav-menu {
-  padding: 0;
-  margin: 0;
-  list-style: none;
-  height: 56px;
-
-  li {
-    display: inline-block;
-
-    a {
-      padding: 14px;
-      font-size: 15px;
-      line-height: 28px;
-      color: #959494;
-      border-bottom: 2px solid transparent;
-
-      &:hover, &:active, &:focus {
-        text-decoration: none;
-        outline: none;
-      }
-    }
-
-    &.active a {
-      color: #616060;
-      border-bottom: 2px solid #4688f1;
-    }
-
-    .badge {
-      font-weight: normal;
-      background-color: #eee;
-      color: #78a;
-    }
-  }
-}
diff --git a/app/assets/stylesheets/framework/mobile.scss b/app/assets/stylesheets/framework/mobile.scss
index c00709fb6bb9a3bb1d7f07080ed77ff2502610a1..0997dfc287c66a648a2535ddcc207c6613fc4a12 100644
--- a/app/assets/stylesheets/framework/mobile.scss
+++ b/app/assets/stylesheets/framework/mobile.scss
@@ -9,7 +9,7 @@
     padding-right: 5px;
   }
 
-  .nav.nav-tabs > li > a {
+  .nav-links > li > a {
     padding: 10px;
     font-size: 12px;
     margin-right: 3px;
@@ -81,7 +81,7 @@
     display: none;
   }
 
-  .center-top-menu, .left-top-menu {
+  .nav-links, .nav-links {
     li a {
       font-size: 14px;
       padding: 19px 10px;
@@ -100,11 +100,6 @@
 }
 
 @media (max-width: $screen-sm-max) {
-  .page-with-sidebar .content-wrapper {
-    padding: 0;
-    padding-top: 1px;
-  }
-
   .issues-filters {
     .milestone-filter, .labels-filter {
       display: none;
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
new file mode 100644
index 0000000000000000000000000000000000000000..c537d97fb245703964787428eea36869c13a55ab
--- /dev/null
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -0,0 +1,39 @@
+.nav-links {
+  padding: 0;
+  margin: 0;
+  list-style: none;
+  height: auto;
+  border-bottom: 1px solid $border-color;
+
+  li {
+    display: inline-block;
+
+    a {
+      display: inline-block;
+      padding: 14px;
+      padding-top: $gl-padding;
+      padding-bottom: 11px;
+      margin-bottom: -1px;
+      font-size: 15px;
+      line-height: 28px;
+      color: #959494;
+      border-bottom: 2px solid transparent;
+
+      &:hover, &:active, &:focus {
+        text-decoration: none;
+        outline: none;
+      }
+    }
+
+    &.active a {
+      color: #000000;
+      border-bottom: 2px solid #4688f1;
+    }
+
+    .badge {
+      font-weight: normal;
+      background-color: #eee;
+      color: #78a;
+    }
+  }
+}
diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss
index 2cd30491bf5de471ee1c144a0f693bb98d7840e6..b6f21fd8c91a20904798eee5dadb77d1817c116c 100644
--- a/app/assets/stylesheets/framework/pagination.scss
+++ b/app/assets/stylesheets/framework/pagination.scss
@@ -1,35 +1,11 @@
 .gl-pagination {
+  text-align: center;
   border-top: 1px solid $border-color;
-  background-color: $background-color;
-  margin: -$gl-padding;
+  margin: 0;
   margin-top: 0;
 
   .pagination {
     padding: 0;
-    margin: 0;
-    display: block;
-
-    li.first,
-    li.last,
-    li.next,
-    li.prev {
-      > a {
-        color: $link-color;
-
-        &:hover {
-          color: #fff;
-        }
-      }
-    }
-
-    li > a,
-    li > span {
-      border: none;
-      margin: 0;
-      @include border-radius(0 !important);
-      padding: 13px 19px;
-      border-right: 1px solid $border-color;
-    }
   }
 }
 
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index af145191bc8aa4f6b9c940e60f6305e035e9e0ef..3ee3443e3499ee99b36fe41c529eb53e8743986f 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -3,8 +3,8 @@
   .select2-choice {
     background: #FFF;
     border-color: #DDD;
-    height: 42px;
-    padding: 8px $gl-padding;
+    height: 36px;
+    padding: 6px $gl-padding;
     font-size: $gl-font-size;
     line-height: 1.42857143;
 
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 83243dd24576d4464f4861c9755954d6cc5aeb69..540d0b0316351dcffb8c22651c2494c5af868eec 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -21,11 +21,10 @@
 
 .content-wrapper {
   width: 100%;
-  padding: 20px;
 
   .container-fluid {
     background: #FFF;
-    padding: $gl-padding;
+    padding: 0 $gl-padding;
 
     &.container-blank {
       background: none;
diff --git a/app/assets/stylesheets/framework/tables.scss b/app/assets/stylesheets/framework/tables.scss
index 793ab3d9bb9bf50e50a3953864233d5ea4f58df5..c4e9f467ce4ea4ba73115ca5ba4b91de16232667 100644
--- a/app/assets/stylesheets/framework/tables.scss
+++ b/app/assets/stylesheets/framework/tables.scss
@@ -1,13 +1,11 @@
 .table-holder {
-  margin: -$gl-padding;
-  margin-top: 0;
-  margin-bottom: 0;
+  margin: 0;
 }
 
 table {
   &.table {
     margin-bottom: $gl-padding;
-    
+
     .dropdown-menu a {
       text-decoration: none;
     }
@@ -32,6 +30,7 @@ table {
       }
 
       th {
+        background-color: $background-color;
         font-weight: normal;
         font-size: 15px;
         border-bottom: 1px solid $border-color !important;
diff --git a/app/assets/stylesheets/framework/timeline.scss b/app/assets/stylesheets/framework/timeline.scss
index ff41e26ed8adc4185f783f34a567820392d27e44..47b843e5e3d585660231546a0d0b97c024f66e75 100644
--- a/app/assets/stylesheets/framework/timeline.scss
+++ b/app/assets/stylesheets/framework/timeline.scss
@@ -5,10 +5,8 @@
   padding: 0;
 
   .timeline-entry {
-    padding: $gl-padding;
+    padding: $gl-padding 0;
     border-color: $table-border-color;
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
     color: $gl-gray;
     border-bottom: 1px solid $border-white-light;
 
diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss
index 94f0ed761dfbd25cb8307e8ac909de54c3c601b3..88072606bf58686a7a08155e396cc70d45b881db 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap.scss
@@ -99,47 +99,6 @@
   }
 }
 
-// Nav tabs
-.nav.nav-tabs {
-  margin-bottom: 15px;
-
-  li {
-    > a {
-      margin-right: 5px;
-      line-height: 20px;
-      border-color: #EEE;
-      color: #888;
-      border-bottom: 1px solid #ddd;
-      .badge {
-        background-color: #eee;
-        color: #888;
-        text-shadow: 0 1px 1px #fff;
-      }
-      i.fa {
-        line-height: 14px;
-      }
-    }
-    &.active {
-      > a {
-        border-color: #CCC;
-        border-bottom: 1px solid #fff;
-        color: #333;
-        font-weight: bold;
-      }
-    }
-  }
-}
-
-.nav-tabs > li > a,
-.nav-pills > li > a {
-  color: #666;
-}
-
-.nav-pills > .active > a > span > .badge {
-  background-color: #fff;
-  color: $gl-primary;
-}
-
 
 /**
  * fix to keep tooltips position in top navigation bar
diff --git a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
index 63868a34e2a1aa4855e561f25540ae746683f8af..798cd224ad0359c733367280c4f09c96bb402542 100644
--- a/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
+++ b/app/assets/stylesheets/framework/tw_bootstrap_variables.scss
@@ -46,7 +46,7 @@ $font-size-base:         $gl-font-size;
 //
 //## Define common padding and border radius sizes and more. Values based on 14px text and 1.428 line-height (~20px to start).
 
-$padding-base-vertical:     9px;
+$padding-base-vertical:     $gl-vert-padding;
 $padding-base-horizontal:   $gl-padding;
 $component-active-color:    #fff;
 $component-active-bg:       $brand-info;
@@ -66,20 +66,20 @@ $legend-color:                   $text-color;
 //##
 
 $pagination-color:                     $gl-gray;
-$pagination-bg:                        $background-color;
-$pagination-border:                    transparent;
+$pagination-bg:                        #fff;
+$pagination-border:                    $border-color;
 
-$pagination-hover-color:               #fff;
-$pagination-hover-bg:                  $brand-info;
-$pagination-hover-border:              transparent;
+$pagination-hover-color:               $gl-gray;
+$pagination-hover-bg:                  $hover;
+$pagination-hover-border:              $border-color;
 
-$pagination-active-color:              #fff;
-$pagination-active-bg:                 $brand-info;
-$pagination-active-border:             transparent;
+$pagination-active-color:              $blue-dark;
+$pagination-active-bg:                 #fff;
+$pagination-active-border:             $border-color;
 
-$pagination-disabled-color:            #fff;
-$pagination-disabled-bg:               lighten($brand-info, 15%);
-$pagination-disabled-border:           transparent;
+$pagination-disabled-color:            #cdcdcd;
+$pagination-disabled-bg:               $background-color;
+$pagination-disabled-border:           $border-color;
 
 
 //== Form states and alerts
diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss
index 714369d9f152a36f4816c45d6f21157559b8daa6..ab4f71af039d1591c413d88449c544465a4761a3 100644
--- a/app/assets/stylesheets/framework/typography.scss
+++ b/app/assets/stylesheets/framework/typography.scss
@@ -177,7 +177,7 @@ body {
 }
 
 .page-title {
-  margin-top: 0px;
+  margin-top: $gl-padding;
   line-height: 1.3;
   font-size: 1.25em;
   font-weight: 600;
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index af75123b0af367508648ead871ee196e231dc049..3ec48da9a41e9a9e191deab621c00cf1eb214387 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -22,8 +22,11 @@ $header-height: 58px;
 $fixed-layout-width: 1280px;
 $gl-gray: #5a5a5a;
 $gl-padding: 16px;
+$gl-vert-padding: 6px;
 $gl-padding-top:10px;
 $gl-avatar-size: 46px;
+$secondary-text: #7f8fa4;
+$error-exclamation-point: #E62958;
 
 /*
  * Color schema
diff --git a/app/assets/stylesheets/framework/zen.scss b/app/assets/stylesheets/framework/zen.scss
index 002bd7e8ca5831669f33dd3045bd781a93e5b828..c3f27333fadab1570a73a4379e9fe801b0b02f26 100644
--- a/app/assets/stylesheets/framework/zen.scss
+++ b/app/assets/stylesheets/framework/zen.scss
@@ -4,7 +4,7 @@
     position: absolute;
     top: 0px;
     right: 4px;
-    line-height: 40px;
+    line-height: 56px;
   }
 
   a.js-zen-leave {
diff --git a/app/assets/stylesheets/highlight/dark.scss b/app/assets/stylesheets/highlight/dark.scss
index 6a2b25ddc67905a29767100b831a84fe5a456be2..8201735beb5573f01783792dae77cfe80158881f 100644
--- a/app/assets/stylesheets/highlight/dark.scss
+++ b/app/assets/stylesheets/highlight/dark.scss
@@ -90,4 +90,22 @@
   .vg { color: #cc6666 } /* Name.Variable.Global */
   .vi { color: #cc6666 } /* Name.Variable.Instance */
   .il { color: #de935f } /* Literal.Number.Integer.Long */
+
+  .line_holder {
+    &.parallel .new.new_line,
+    &.parallel .new.line_content,
+    &.new .old_line,
+    &.new .new_line,
+    &.new .line_content {
+      @include diff_background(255, 255, 255, #808080);
+    }
+
+    &.parallel .old.old_line,
+    &.parallel .old.line_content,
+    &.old .old_line,
+    &.old .new_line,
+    &.old .line_content {
+      @include diff_background(255, 51, 51, #808080);
+    }
+  }
 }
diff --git a/app/assets/stylesheets/highlight/monokai.scss b/app/assets/stylesheets/highlight/monokai.scss
index 8560c3c490fc1e3070148bb05828d97bbfee3ced..cc03ed6ae454b43d6eb75e25ae28ff2e2a00818c 100644
--- a/app/assets/stylesheets/highlight/monokai.scss
+++ b/app/assets/stylesheets/highlight/monokai.scss
@@ -90,4 +90,22 @@
   .gu { color: #75715e; } /* Generic.Subheading & Diff Unified/Comment? */
   .gd { color: #f92672; } /* Generic.Deleted & Diff Deleted */
   .gi { color: #a6e22e; } /* Generic.Inserted & Diff Inserted */
+
+  .line_holder {
+    &.parallel .new.new_line,
+    &.parallel .new.line_content,
+    &.new .old_line,
+    &.new .new_line,
+    &.new .line_content {
+      @include diff_background(156, 175, 183, #808080);
+    }
+
+    &.parallel .old.old_line,
+    &.parallel .old.line_content,
+    &.old .old_line,
+    &.old .new_line,
+    &.old .line_content {
+      @include diff_background(254, 147, 140, #808080);
+    }
+  }
 }
diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss
index 7d489a9666b8b0828a5b7f4bf7670b0d6cdb23a4..fdfac6cd2494f6bc1739c8203ab6a637e18f203b 100644
--- a/app/assets/stylesheets/highlight/solarized_dark.scss
+++ b/app/assets/stylesheets/highlight/solarized_dark.scss
@@ -111,4 +111,22 @@
   .vg { color: #268bd2 } /* Name.Variable.Global */
   .vi { color: #268bd2 } /* Name.Variable.Instance */
   .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+
+  .line_holder {
+    &.parallel .new.new_line,
+    &.parallel .new.line_content,
+    &.new .old_line,
+    &.new .new_line,
+    &.new .line_content {
+      @include diff_background(255, 255, 255, #808080);
+    }
+
+    &.parallel .old.old_line,
+    &.parallel .old.line_content,
+    &.old .old_line,
+    &.old .new_line,
+    &.old .line_content {
+      @include diff_background(255, 51, 51, #808080);
+    }
+  }
 }
diff --git a/app/assets/stylesheets/highlight/solarized_light.scss b/app/assets/stylesheets/highlight/solarized_light.scss
index 200ed346446e1dc110c09563a2a872914323f564..f9788951aa840ad9d59b0fab6fa4ff7023bb9aa1 100644
--- a/app/assets/stylesheets/highlight/solarized_light.scss
+++ b/app/assets/stylesheets/highlight/solarized_light.scss
@@ -111,4 +111,23 @@
   .vg { color: #268bd2 } /* Name.Variable.Global */
   .vi { color: #268bd2 } /* Name.Variable.Instance */
   .il { color: #2aa198 } /* Literal.Number.Integer.Long */
+
+
+  .line_holder {
+    &.parallel .new.new_line,
+    &.parallel .new.line_content,
+    &.new .old_line,
+    &.new .new_line,
+    &.new .line_content {
+      @include diff_background(92, 164, 169, #FAF3DD);
+    }
+
+    &.parallel .old.old_line,
+    &.parallel .old.line_content,
+    &.old .old_line,
+    &.old .new_line,
+    &.old .line_content {
+      @include diff_background(237, 106, 90, #FAF3DD);
+    }
+  }
 }
diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss
index 17245d3be7ba4d00a96694e81d2df244e2605303..e53d6fc6bdc185996d113afe500466c2e7a87332 100644
--- a/app/assets/stylesheets/pages/commit.scss
+++ b/app/assets/stylesheets/pages/commit.scss
@@ -35,6 +35,8 @@
 }
 
 .commit-box {
+  border-top: 1px solid $border-color;
+
   .commit-title {
     margin: 0;
     font-size: 23px;
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 800df95cff306fac1c3a2c7b9b0001948131efc3..818fd03e2ae21164dcd37dc66136a14f0c19222e 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -36,6 +36,10 @@ li.commit {
     line-height: 20px;
     margin-bottom: 2px;
 
+    .btn-clipboard {
+      margin-top: -1px;
+    }
+
     .notes_count {
       float: right;
       margin-right: 10px;
diff --git a/app/assets/stylesheets/pages/detail_page.scss b/app/assets/stylesheets/pages/detail_page.scss
index deab805dbc2988ab003f6f5b47675e7cb1697a92..529a43548c80b4b9dec44a0686db48b2e76a1b9f 100644
--- a/app/assets/stylesheets/pages/detail_page.scss
+++ b/app/assets/stylesheets/pages/detail_page.scss
@@ -1,7 +1,5 @@
 .detail-page-header {
-  margin: -$gl-padding;
-  padding: 7px $gl-padding;
-  margin-bottom: 0px;
+  padding: 11px 0;
   border-bottom: 1px solid $border-color;
   color: #5c5d5e;
   font-size: 16px;
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index afd6fb73675c36541b65e95f5a2a6b460c4656fe..0b79aa172a47ea31e5d63146de8eb32f0abcc470 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -1,9 +1,7 @@
 // Common
 .diff-file {
-  margin-left: -$gl-padding;
-  margin-right: -$gl-padding;
-  border: none;
-  border-bottom: 1px solid #E7E9EE;
+  border: 1px solid $border-color;
+  border-top: none;
 
   .diff-header {
     position: relative;
@@ -23,14 +21,6 @@
       }
     }
 
-    .diff-controls {
-      .btn {
-        padding: 0px 10px;
-        font-size: 13px;
-        line-height: 28px;
-      }
-    }
-
     .commit-short-id {
       font-family: $monospace_font;
       font-size: smaller;
@@ -76,6 +66,7 @@
       width: 100%;
       font-family: $monospace_font;
       border: none;
+      border-collapse: separate;
       margin: 0px;
       padding: 0px;
       .line_holder td {
@@ -402,3 +393,18 @@
     right: 15px;
   }
 }
+
+@mixin diff_background($r, $g, $b, $custom-border) {
+  /* Fallback for web browsers that doesn't support RGBa */
+  background: rgb($r, $g, $b);
+  /* RGBa with 0.3 opacity */
+  background: rgba($r, $g, $b, 0.3);
+
+  &.new_line, &.old_line {
+    border-right-color: $custom-border !important;
+  }
+
+  &.line_content span.idiff {
+    background: rgb($r, $g, $b);
+  }
+}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 984b4b91216c3b45463cdaa79d0d7e0f3003b862..8fa15b35748cdc1799281420d035e9c070b6ee67 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -4,9 +4,7 @@
  */
 .event-item {
   font-size: $gl-font-size;
-  padding: $gl-padding $gl-padding $gl-padding ($gl-padding + $gl-avatar-size + 15px);
-  margin-left: -$gl-padding;
-  margin-right: -$gl-padding;
+  padding: $gl-padding 0 $gl-padding ($gl-avatar-size + 15px);
   border-bottom: 1px solid $table-border-color;
   color: #7f8fa4;
 
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index d4b44004f4fb1e7a84b5d90c6e2aee6acd6af9c6..977ada0ff38a2efcfcb99b54c85e448f741044ca 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -20,6 +20,11 @@
       position: fixed;
       top: 70px;
       margin-right: 35px;
+
+      &.no-affix {
+        position: relative;
+        top: 0;
+      }
     }
   }
 }
@@ -27,10 +32,10 @@
 .project-issuable-filter {
   .controls {
     float: right;
-    margin-top: 7px;
+    margin-top: 11px;
   }
 
-  .center-top-menu {
+  .nav-links {
     text-align: left;
   }
 }
@@ -95,7 +100,7 @@
 
   .cross-project-reference {
     color: $gl-link-color;
-    
+
     span {
       white-space: nowrap;
       width: 85%;
@@ -105,8 +110,13 @@
       text-overflow: ellipsis;
     }
 
+    cite {
+      font-style: normal;
+    }
+
     button {
       float: right;
+      padding: 3px 5px;
     }
   }
 
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index a02a3a72e79fc54bd8f1fa865e79c65823f29064..ad92cc22815664622b3b13d75131e461970be180 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -6,7 +6,7 @@
     .issue-title {
       margin-bottom: 5px;
       font-size: $list-font-size;
-      font-weight: bold;
+      font-weight: 600;
     }
 
     .issue-info {
@@ -144,3 +144,8 @@ form.edit-issue {
 .issue-form .select2-container {
   width: 250px !important;
 }
+
+.issue-closed-by-widget {
+  color: $secondary-text;
+  margin-left: 52px;
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index 82effde0bf3b49e8bb8aed35f1c2387292dfc6ea..f033ff15f881194de18202f67a7d5024d4aac43e 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -3,9 +3,9 @@
  *
  */
 .mr-state-widget {
-  background: #F7F8FA;
+  background: $background-color;
   color: $gl-gray;
-  border: 1px solid #dce0e6;
+  border: 1px solid $border-color;
   @include border-radius(2px);
 
   form {
@@ -150,7 +150,7 @@
     .merge-request-title {
       margin-bottom: 5px;
       font-size: $list-font-size;
-      font-weight: bold;
+      font-weight: 600;
     }
 
     .merge-request-info {
@@ -201,3 +201,39 @@
 .mr-source-target {
   line-height: 31px;
 }
+
+.disabled-comment-area {
+  padding: 16px 0;
+
+  .disabled-profile {
+    width: 40px;
+    height: 40px;
+    background: $border-gray-dark;
+    border-radius: 20px;
+    display: inline-block;
+    margin-right: 10px;
+  }
+
+  .disabled-comment {
+    background: $gray-light;
+    display: inline-block;
+    vertical-align: top;
+    height: 200px;
+    border-radius: 4px;
+    border: 1px solid $border-gray-normal;
+    padding-top: 90px;
+    text-align: center;
+    right: 20px;
+    position: absolute;
+    left: 70px;
+    margin-bottom: 20px;
+
+    span {
+      color: #B2B2B2;
+
+      a {
+        color: $md-link-color;
+      }
+    }
+  }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index d86259f93fbcfe672cf0fab9d84d03ad196a21fe..2c9a42f9892eee20c5b23190b7886d354c301837 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -159,6 +159,7 @@
 .edit_note {
   .markdown-area {
     min-height: 140px;
+    max-height: 430px;
   }
   .note-form-actions {
     background: transparent;
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index d32509b7d490bde14254fb03b85afae1c0e5bcd3..003a4c22f206af057a7d1d6407a83b93d35e0cd7 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -26,6 +26,8 @@
 }
 
 .project-home-panel {
+  padding-bottom: 40px;
+  border-bottom: 1px solid $border-color;
 
   .cover-controls {
     .project-settings-dropdown {
@@ -51,6 +53,8 @@
   }
 
   .notifications-btn {
+    margin-top: -28px;
+
     .fa-bell {
       margin-right: 6px;
     }
@@ -75,17 +79,6 @@
     }
   }
 
-  .git-clone-holder {
-    max-width: 498px;
-
-    .form-control {
-      background: #FFF;
-      font-size: 14px;
-      height: 42px;
-      margin-left: -1px;
-    }
-  }
-
   .visibility-level-label {
     @extend .btn;
     @extend .btn-gray;
@@ -98,24 +91,31 @@
     }
   }
 
-  .git-clone-holder {
-    display: inline-table;
-    position: relative;
-  }
-
   .project-repo-buttons {
-    margin-top: 12px;
+    margin-top: 20px;
     margin-bottom: 0px;
 
     .count-buttons {
       display: block;
-      margin-bottom: 12px;
+      margin-bottom: 20px;
+    }
+
+    .clone-row {
+      .split-repo-buttons,
+      .project-clone-holder {
+        display: inline-block;
+      }
+
+      .split-repo-buttons {
+        margin: 0 12px;
+      }
     }
 
     .btn {
       @include btn-gray;
       text-transform: none;
     }
+
     .count-with-arrow {
       display: inline-block;
       position: relative;
@@ -160,10 +160,10 @@
         border-style: solid;
         font-size: 13px;
         font-weight: 600;
-        line-height: 20px;
-        padding: 11px 16px;
+        line-height: 13px;
+        padding: $gl-vert-padding $gl-padding;
         letter-spacing: .4px;
-        padding: 10px;
+        padding: 10px 14px;
         text-align: center;
         vertical-align: middle;
         touch-action: manipulation;
@@ -189,118 +189,6 @@
   }
 }
 
-.git-clone-holder {
-  .project-home-dropdown + & {
-    margin-right: 45px;
-  }
-
-  .clone-options {
-    display: table-cell;
-    a.btn {
-      width: 100%;
-    }
-  }
-
-  .form-control {
-    cursor: auto;
-    @extend .monospace;
-    background: #FAFAFA;
-    width: 101%;
-  }
-
-  .input-group-addon {
-    background: #f7f8fa;
-
-    &.git-protocols {
-      padding: 0;
-      border: none;
-
-      .input-group-btn:last-child > .btn {
-        @include border-radius-right(0);
-
-        border-left: 1px solid #c6cacf;
-        margin-left: -2px !important;
-      }
-    }
-  }
-}
-
-.projects-search-form {
-
-  .input-group .form-control {
-    height: 42px;
-  }
-}
-
-.input-group-btn {
-  .btn {
-    @include btn-gray;
-    @include btn-middle;
-
-    &:hover {
-      outline: none;
-    }
-
-    &:focus {
-      outline: none;
-    }
-
-    &:active {
-      outline: none;
-    }
-
-    &.btn-clipboard {
-      padding-left: 15px;
-      padding-right: 15px;
-    }
-  }
-
-  .active {
-    @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
-
-    border: 1px solid #c6cacf !important;
-    background-color: #e4e7ed !important;
-  }
-
-  .btn-green {
-    @include btn-green
-  }
-
-}
-
-.split-repo-buttons {
-  display: inline-table;
-  margin: 0 12px 0 12px;
-
-  .btn{
-    @include btn-gray;
-    @include btn-default;
-  }
-
-  .dropdown-toggle  {
-    margin: -5px;
-  }
-}
-
-#notification-form {
-  margin-left: 5px;
-}
-
-.dropdown-new {
-  margin-left: -5px;
-}
-
-.open > .dropdown-new.btn {
-  @include box-shadow(inset 0 0 4px rgba(0, 0, 0, 0.12));
-
-  border: 1px solid #c6cacf !important;
-  background-color: #e4e7ed !important;
-  text-transform: uppercase;
-  color: #313236 !important;
-  font-size: 13px;
-  font-weight: 600;
-}
-
 .dropdown-menu {
   @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);
@@ -352,28 +240,6 @@
   color: #555;
 }
 
-ul.nav.nav-projects-tabs {
-  @extend .nav-tabs;
-
-  padding-left: 8px;
-
-  li {
-    a {
-      padding: 6px 25px;
-      margin-top: 2px;
-      border-color: #DDD;
-      background-color: #EEE;
-      text-shadow: 0 1px 1px white;
-      color: #555;
-    }
-    &.active {
-      a {
-        font-weight: bold;
-      }
-    }
-  }
-}
-
 .project_member_row form {
   margin: 0px;
 }
@@ -400,9 +266,9 @@ ul.nav.nav-projects-tabs {
 
 .breadcrumb.repo-breadcrumb {
   padding: 0;
-  line-height: 42px;
   background: transparent;
   border: none;
+  line-height: 42px;
   margin: 0;
 
   > li + li:before {
@@ -417,11 +283,8 @@ ul.nav.nav-projects-tabs {
 
 .top-area {
   border-bottom: 1px solid #EEE;
-  margin: 0 -16px;
-  padding: 0 $gl-padding;
-  height: 42px;
 
-  ul.left-top-menu {
+  ul.nav-links {
     display: inline-block;
     width: 50%;
     margin-bottom: 0px;
@@ -432,12 +295,12 @@ ul.nav.nav-projects-tabs {
     width: 50%;
     display: inline-block;
     float: right;
-    padding-top: 7px;
+    padding-top: 11px;
     text-align: right;
 
     .btn-green {
-      margin-top: -2px;
       margin-left: 10px;
+      float: right;
     }
   }
 
@@ -483,11 +346,11 @@ table.table.protected-branches-list tr.no-border {
   padding-top: 10px;
   padding-bottom: 4px;
 
-  ul.nav-pills {
+  ul.nav {
     display:inline-block;
   }
 
-  .nav-pills li {
+  .nav li {
     display:inline;
   }
 
@@ -524,8 +387,7 @@ pre.light-well {
 }
 
 .projects-search-form {
-  margin: -$gl-padding;
-  padding: $gl-padding;
+  padding: $gl-padding 0;
   padding-bottom: 0;
   margin-bottom: 0px;
 
@@ -575,10 +437,8 @@ pre.light-well {
   @include basic-list;
 
   .project-row {
-    padding: $gl-padding;
+    padding: $gl-padding 0;
     border-color: $table-border-color;
-    margin-left: -$gl-padding;
-    margin-right: -$gl-padding;
 
     &.no-description {
       .project {
@@ -632,8 +492,6 @@ pre.light-well {
 }
 
 .project-last-commit {
-  margin: 0 7px;
-
   .ci-status {
     margin-right: 16px;
   }
@@ -663,9 +521,7 @@ pre.light-well {
 }
 
 .project-show-readme .readme-holder {
-  margin-left: -$gl-padding;
-  margin-right: -$gl-padding;
-  padding: ($gl-padding + 7px);
+  padding: $gl-padding 0;
   border-top: 0;
 
   .edit-project-readme {
@@ -673,3 +529,38 @@ pre.light-well {
     position: relative;
   }
 }
+
+.git-clone-holder {
+  width: 498px;
+
+  .btn-clipboard {
+    border: 1px solid $border-color;
+    padding: 6px $gl-padding;
+  }
+
+  .project-home-dropdown + & {
+    margin-right: 45px;
+  }
+
+  .clone-options {
+    display: table-cell;
+    a.btn {
+      width: 100%;
+    }
+  }
+
+  .form-control {
+    @extend .monospace;
+    background: #FFF;
+    font-size: 14px;
+    margin-left: -1px;
+    cursor: auto;
+    width: 101%;
+  }
+}
+
+.cannot-be-merged, 
+.cannot-be-merged:hover {
+  color: #E62958;
+  margin-top: 2px;
+}
diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss
index 97505edeabf53fae1007be54c38cae70a1ea819a..6a6dd7dfc853c8cf9b2d97b6d31e4ce8f7ccb9c5 100644
--- a/app/assets/stylesheets/pages/tree.scss
+++ b/app/assets/stylesheets/pages/tree.scss
@@ -1,4 +1,7 @@
 .tree-holder {
+  > .nav-block {
+    margin: 11px 0;
+  }
 
   .file-finder {
     width: 50%;
@@ -13,7 +16,7 @@
 
     tr {
       > td, > th {
-        line-height: 28px;
+        line-height: 26px;
       }
 
       &:hover {
@@ -86,12 +89,14 @@
 
 .blob-commit-info {
   list-style: none;
+  padding: $gl-padding;
+  background: $background-color;
+  border: 1px solid $border-color;
+  border-bottom: none;
   margin: 0;
-  padding: 0;
-  margin-bottom: 5px;
 
   .commit {
-    padding: $gl-padding 0;
+    padding: 0;
 
     .commit-row-title {
       .commit-row-message {
@@ -115,3 +120,8 @@
   font-weight: normal;
   color: $md-link-color;
 }
+
+.tree-controls {
+  float: right;
+  margin-top: 11px;
+}
diff --git a/app/controllers/abuse_reports_controller.rb b/app/controllers/abuse_reports_controller.rb
index 38814459f660c42d4409762caccf85d503025445..2eac0cabf7a01a1defd5919ed78bd7fd6d7d1fa0 100644
--- a/app/controllers/abuse_reports_controller.rb
+++ b/app/controllers/abuse_reports_controller.rb
@@ -2,6 +2,7 @@ class AbuseReportsController < ApplicationController
   def new
     @abuse_report = AbuseReport.new
     @abuse_report.user_id = params[:user_id]
+    @ref_url = params.fetch(:ref_url, '')
   end
 
   def create
diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb
index 38a5a9fca08e36f30c77ee9c9b9cfd858297a250..2463cfa87be6dcff55e93085ae8d4eee28c82b4c 100644
--- a/app/controllers/admin/abuse_reports_controller.rb
+++ b/app/controllers/admin/abuse_reports_controller.rb
@@ -6,11 +6,9 @@ class Admin::AbuseReportsController < Admin::ApplicationController
   def destroy
     abuse_report = AbuseReport.find(params[:id])
 
-    if params[:remove_user]
-      abuse_report.user.destroy
-    end
-
+    abuse_report.remove_user if params[:remove_user]
     abuse_report.destroy
+
     render nothing: true
   end
 end
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index 44d06b6a64787b488a3df6dac30c9446f3ea6d6d..094eef28a4396eb508120dd7ea9c3853e1fb9cd5 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -73,9 +73,14 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
       :metrics_pool_size,
       :metrics_timeout,
       :metrics_method_call_threshold,
+      :metrics_sample_interval,
+      :ip_blocking_enabled,
+      :dnsbl_servers_list,
       :recaptcha_enabled,
       :recaptcha_site_key,
       :recaptcha_private_key,
+      :sentry_enabled,
+      :sentry_dsn,
       restricted_visibility_levels: [],
       import_sources: []
     )
diff --git a/app/controllers/admin/broadcast_messages_controller.rb b/app/controllers/admin/broadcast_messages_controller.rb
index 497c34f8f493bd9dfa31dc67656dbf5deb6b5151..4735b27c65d31b21a40fec21bf11aded11cd57f7 100644
--- a/app/controllers/admin/broadcast_messages_controller.rb
+++ b/app/controllers/admin/broadcast_messages_controller.rb
@@ -1,8 +1,12 @@
 class Admin::BroadcastMessagesController < Admin::ApplicationController
-  before_action :broadcast_messages
+  before_action :finder, only: [:edit, :update, :destroy]
 
   def index
-    @broadcast_message = BroadcastMessage.new
+    @broadcast_messages = BroadcastMessage.reorder("starts_at ASC").page(params[:page])
+    @broadcast_message  = BroadcastMessage.new
+  end
+
+  def edit
   end
 
   def create
@@ -15,8 +19,16 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
     end
   end
 
+  def update
+    if @broadcast_message.update(broadcast_message_params)
+      redirect_to admin_broadcast_messages_path, notice: 'Broadcast Message was successfully updated.'
+    else
+      render :edit
+    end
+  end
+
   def destroy
-    BroadcastMessage.find(params[:id]).destroy
+    @broadcast_message.destroy
 
     respond_to do |format|
       format.html { redirect_back_or_default(default: { action: 'index' }) }
@@ -26,14 +38,17 @@ class Admin::BroadcastMessagesController < Admin::ApplicationController
 
   protected
 
-  def broadcast_messages
-    @broadcast_messages ||= BroadcastMessage.order("starts_at DESC").page(params[:page])
+  def finder
+    @broadcast_message = BroadcastMessage.find(params[:id])
   end
 
   def broadcast_message_params
-    params.require(:broadcast_message).permit(
-      :alert_type, :color, :ends_at, :font,
-      :message, :starts_at
-    )
+    params.require(:broadcast_message).permit(%i(
+      color
+      ends_at
+      font
+      message
+      starts_at
+    ))
   end
 end
diff --git a/app/controllers/admin/identities_controller.rb b/app/controllers/admin/identities_controller.rb
index e383fe38ea68352c5ba172a396a978f805a27527..79a53556f0a30700bf9ed1b79c4df99efcb1c22c 100644
--- a/app/controllers/admin/identities_controller.rb
+++ b/app/controllers/admin/identities_controller.rb
@@ -26,6 +26,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
 
   def update
     if @identity.update_attributes(identity_params)
+      RepairLdapBlockedUserService.new(@user).execute
       redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully updated.'
     else
       render :edit
@@ -34,6 +35,7 @@ class Admin::IdentitiesController < Admin::ApplicationController
 
   def destroy
     if @identity.destroy
+      RepairLdapBlockedUserService.new(@user).execute
       redirect_to admin_user_identities_path(@user), notice: 'User identity was successfully removed.'
     else
       redirect_to admin_user_identities_path(@user), alert: 'Failed to remove user identity.'
diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb
index d7c927d444cbc4b43d0d5bfe20f63bb8e2c5a5c8..87f4fb455b8e14ff60980bd88dbf8b5a341ee31b 100644
--- a/app/controllers/admin/users_controller.rb
+++ b/app/controllers/admin/users_controller.rb
@@ -40,7 +40,9 @@ class Admin::UsersController < Admin::ApplicationController
   end
 
   def unblock
-    if user.activate
+    if user.ldap_blocked?
+      redirect_back_or_admin_user(alert: "This user cannot be unlocked manually from GitLab")
+    elsif user.activate
       redirect_back_or_admin_user(notice: "Successfully unblocked")
     else
       redirect_back_or_admin_user(alert: "Error occurred. User was not unblocked")
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index 81cb1367e2cba6421509148d2005d7c453af6cbd..2d735b905970f648c34f19c31c2df77e4a25fca5 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -15,6 +15,7 @@ class ApplicationController < ActionController::Base
   before_action :check_password_expiration
   before_action :check_2fa_requirement
   before_action :ldap_security_check
+  before_action :sentry_user_context
   before_action :default_headers
   before_action :add_gon_variables
   before_action :configure_permitted_parameters, if: :devise_controller?
@@ -24,6 +25,7 @@ class ApplicationController < ActionController::Base
 
   helper_method :abilities, :can?, :current_application_settings
   helper_method :import_sources_enabled?, :github_import_enabled?, :github_import_configured?, :gitlab_import_enabled?, :gitlab_import_configured?, :bitbucket_import_enabled?, :bitbucket_import_configured?, :gitorious_import_enabled?, :google_code_import_enabled?, :fogbugz_import_enabled?, :git_import_enabled?
+  helper_method :repository
 
   rescue_from Encoding::CompatibilityError do |exception|
     log_exception(exception)
@@ -41,6 +43,16 @@ class ApplicationController < ActionController::Base
 
   protected
 
+  def sentry_user_context
+    if Rails.env.production? && current_application_settings.sentry_enabled && current_user
+      Raven.user_context(
+        id: current_user.id,
+        email: current_user.email,
+        username: current_user.username,
+      )
+    end
+  end
+
   # From https://github.com/plataformatec/devise/wiki/How-To:-Simple-Token-Authentication-Example
   # https://gist.github.com/josevalim/fb706b1e933ef01e4fb6
   def authenticate_user_from_token!
@@ -115,7 +127,7 @@ class ApplicationController < ActionController::Base
       #   localhost/group/project
       #
       if id =~ /\.git\Z/
-        redirect_to request.original_url.gsub(/\.git\Z/, '') and return
+        redirect_to request.original_url.gsub(/\.git\/?\Z/, '') and return
       end
 
       project_path = "#{namespace}/#{id}"
diff --git a/app/controllers/ci/lints_controller.rb b/app/controllers/ci/lints_controller.rb
index e782a51e7eb93dce8ca605dd2236cd749a272119..a7af3cb83450b1e6d5fc23559345d7f4accee53f 100644
--- a/app/controllers/ci/lints_controller.rb
+++ b/app/controllers/ci/lints_controller.rb
@@ -6,11 +6,13 @@ module Ci
     end
 
     def create
-      if params[:content].blank?
+      @content = params[:content]
+
+      if @content.blank?
         @status = false
         @error = "Please provide content of .gitlab-ci.yml"
       else
-        @config_processor = Ci::GitlabCiYamlProcessor.new params[:content]
+        @config_processor = Ci::GitlabCiYamlProcessor.new(@content)
         @stages = @config_processor.stages
         @builds = @config_processor.builds
         @status = true
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f159a6d6dc68270194450328fac3320a9733f0e3
--- /dev/null
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -0,0 +1,56 @@
+class Projects::ArtifactsController < Projects::ApplicationController
+  layout 'project'
+  before_action :authorize_read_build_artifacts!
+
+  def download
+    unless artifacts_file.file_storage?
+      return redirect_to artifacts_file.url
+    end
+
+    unless artifacts_file.exists?
+      return render_404
+    end
+
+    send_file artifacts_file.path, disposition: 'attachment'
+  end
+
+  def browse
+    return render_404 unless build.artifacts?
+
+    directory = params[:path] ? "#{params[:path]}/" : ''
+    @entry = build.artifacts_metadata_entry(directory)
+
+    return render_404 unless @entry.exists?
+  end
+
+  def file
+    entry = build.artifacts_metadata_entry(params[:path])
+
+    if entry.exists?
+      render json: { archive: build.artifacts_file.path,
+                     entry: Base64.encode64(entry.path) }
+    else
+      render json: {}, status: 404
+    end
+  end
+
+  private
+
+  def build
+    @build ||= project.builds.unscoped.find_by!(id: params[:build_id])
+  end
+
+  def artifacts_file
+    @artifacts_file ||= build.artifacts_file
+  end
+
+  def authorize_read_build_artifacts!
+    unless can?(current_user, :read_build_artifacts, @project)
+      if current_user.nil?
+        return authenticate_user!
+      else
+        return render_404
+      end
+    end
+  end
+end
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index c56a3497bb2d096cb00231fa979148c0c22c8037..8133de90a41ba845e0fa4bb29fa787791283fbf8 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -65,8 +65,9 @@ class Projects::BlobController < Projects::ApplicationController
   end
 
   def diff
-    @form = UnfoldForm.new(params)
-    @lines = @blob.data.lines[@form.since - 1..@form.to - 1]
+    @form  = UnfoldForm.new(params)
+    @lines = Gitlab::Highlight.highlight_lines(repository, @ref, @path)
+    @lines = @lines[@form.since - 1..@form.to - 1]
 
     if @form.bottom?
       @match_line = ''
diff --git a/app/controllers/projects/builds_controller.rb b/app/controllers/projects/builds_controller.rb
index 39d3ba26ba2ade4d309b0cbc44e7796e1c14c654..92d9699fe849b38267da9eca7c57464c0c9b8f85 100644
--- a/app/controllers/projects/builds_controller.rb
+++ b/app/controllers/projects/builds_controller.rb
@@ -2,7 +2,6 @@ class Projects::BuildsController < Projects::ApplicationController
   before_action :build, except: [:index, :cancel_all]
 
   before_action :authorize_manage_builds!, except: [:index, :show, :status]
-  before_action :authorize_download_build_artifacts!, only: [:download]
 
   layout "project"
 
@@ -43,7 +42,7 @@ class Projects::BuildsController < Projects::ApplicationController
 
   def retry
     unless @build.retryable?
-      return page_404
+      return render_404
     end
 
     build = Ci::Build.retry(@build)
@@ -51,18 +50,6 @@ class Projects::BuildsController < Projects::ApplicationController
     redirect_to build_path(build)
   end
 
-  def download
-    unless artifacts_file.file_storage?
-      return redirect_to artifacts_file.url
-    end
-
-    unless artifacts_file.exists?
-      return not_found!
-    end
-
-    send_file artifacts_file.path, disposition: 'attachment'
-  end
-
   def status
     render json: @build.to_json(only: [:status, :id, :sha, :coverage], methods: :sha)
   end
@@ -79,27 +66,13 @@ class Projects::BuildsController < Projects::ApplicationController
     @build ||= project.builds.unscoped.find_by!(id: params[:id])
   end
 
-  def artifacts_file
-    build.artifacts_file
-  end
-
   def build_path(build)
     namespace_project_build_path(build.project.namespace, build.project, build)
   end
 
   def authorize_manage_builds!
     unless can?(current_user, :manage_builds, project)
-      return page_404
-    end
-  end
-
-  def authorize_download_build_artifacts!
-    unless can?(current_user, :download_build_artifacts, @project)
-      if current_user.nil?
-        return authenticate_user!
-      else
-        return render_404
-      end
+      return render_404
     end
   end
 end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 0aaba3792bf08ead5c260cbe0ba4ceda3b851540..f5a169e5aa984847cc9435914d7745bdc5a88079 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -72,6 +72,7 @@ class Projects::CommitController < Projects::ApplicationController
       @diffs = commit.diffs
     end
 
+    @diff_refs = [commit.parent || commit, commit]
     @notes_count = commit.notes.count
 
     @statuses = ci_commit.statuses if ci_commit
@@ -79,7 +80,7 @@ class Projects::CommitController < Projects::ApplicationController
 
   def authorize_manage_builds!
     unless can?(current_user, :manage_builds, project)
-      return page_404
+      return render_404
     end
   end
 end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index 5200d609cc98c458f19c438fa7c7630456e59573..f8ec76cd4e5763665ef9dfa168080b7d81f467c5 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -21,7 +21,8 @@ class Projects::CompareController < Projects::ApplicationController
       @commits = Commit.decorate(compare_result.commits, @project)
       @diffs = compare_result.diffs
       @commit = @project.commit(head_ref)
-      @first_commit = @project.commit(base_ref)
+      @base_commit = @project.commit(base_ref)
+      @diff_refs = [@base_commit, @commit]
       @line_notes = []
     end
   end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index b59b52291fb0c90da3c27ff177afcb9b6d830d34..68244883803f7f4b5b3ef1bc9bc625831612c8c8 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -49,7 +49,7 @@ class Projects::IssuesController < Projects::ApplicationController
       assignee_id: ""
     )
 
-    @issue = @project.issues.new(issue_params)
+    @issue = @noteable = @project.issues.new(issue_params)
     respond_with(@issue)
   end
 
@@ -61,7 +61,7 @@ class Projects::IssuesController < Projects::ApplicationController
     @note = @project.notes.new(noteable: @issue)
     @notes = @issue.notes.nonawards.with_associations.fresh
     @noteable = @issue
-    @merge_requests = @issue.referenced_merge_requests
+    @merge_requests = @issue.referenced_merge_requests(current_user)
 
     respond_with(@issue)
   end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index de948d271c82df2bc70ceed422c91dfee2fcd0af..ed3050d59aafa73943aa6bfb50ae945e59503241 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -58,7 +58,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
 
   def diffs
     @commit = @merge_request.last_commit
-    @first_commit = @merge_request.first_commit
+    @base_commit = @merge_request.diff_base_commit
+
+    # MRs created before 8.4 don't have a diff_base_commit,
+    # but we need it for the "View file @ ..." link by deleted files
+    @base_commit ||= @merge_request.first_commit.parent || @merge_request.first_commit
 
     @comments_allowed = @reply_allowed = true
     @comments_target = {
@@ -90,6 +94,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
   def new
     params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
     @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
+    @noteable = @merge_request
 
     @target_branches = if @merge_request.target_project
                          @merge_request.target_project.repository.branch_names
@@ -101,7 +106,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
     @source_project = merge_request.source_project
     @commits = @merge_request.compare_commits.reverse
     @commit = @merge_request.last_commit
-    @first_commit = @merge_request.first_commit
+    @base_commit = @merge_request.diff_base_commit
     @diffs = @merge_request.compare_diffs
 
     @ci_commit = @merge_request.ci_commit
diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb
index 2104c7a7a718f7f70b07660407b8e7c7865ed7cc..92b0caa2efb3f50097917b5a248fdd6c50ace19d 100644
--- a/app/controllers/projects/snippets_controller.rb
+++ b/app/controllers/projects/snippets_controller.rb
@@ -25,7 +25,7 @@ class Projects::SnippetsController < Projects::ApplicationController
   end
 
   def new
-    @snippet = @project.snippets.build
+    @snippet = @noteable = @project.snippets.build
   end
 
   def create
diff --git a/app/controllers/registrations_controller.rb b/app/controllers/registrations_controller.rb
index c48175a4c5ab25fb73277740ccab91e9e2df1bef..5efdd613e79c674f4451bc2e1f6e3d6ddcf5faec 100644
--- a/app/controllers/registrations_controller.rb
+++ b/app/controllers/registrations_controller.rb
@@ -8,6 +8,11 @@ class RegistrationsController < Devise::RegistrationsController
 
   def create
     if !Gitlab::Recaptcha.load_configurations! || verify_recaptcha
+      if Gitlab::IpCheck.new(request.remote_ip).spam?
+        flash[:alert] = 'Could not create an account. This IP is listed for spam.'
+        return render action: 'new'
+      end
+
       super
     else
       flash[:alert] = "There was an error with the reCAPTCHA code below. Please re-enter the code."
diff --git a/app/controllers/sent_notifications_controller.rb b/app/controllers/sent_notifications_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7271c933b9b810bc63d83ab38076addeed18faef
--- /dev/null
+++ b/app/controllers/sent_notifications_controller.rb
@@ -0,0 +1,25 @@
+class SentNotificationsController < ApplicationController
+  skip_before_action :authenticate_user!
+
+  def unsubscribe
+    @sent_notification = SentNotification.for(params[:id])
+    return render_404 unless @sent_notification && @sent_notification.unsubscribable?
+
+    noteable = @sent_notification.noteable
+    noteable.unsubscribe(@sent_notification.recipient)
+
+    flash[:notice] = "You have been unsubscribed from this thread."
+    if current_user
+      case noteable
+      when Issue
+        redirect_to issue_path(noteable)
+      when MergeRequest
+        redirect_to merge_request_path(noteable)
+      else
+        redirect_to root_path
+      end
+    else
+      redirect_to new_user_session_path
+    end
+  end
+end
diff --git a/app/finders/issuable_finder.rb b/app/finders/issuable_finder.rb
index 4d56b48e3f8da39fae5aab09a6b8b8448ec92d0b..0a4192e6bacd848cf8bb7e82e961a66be41173f3 100644
--- a/app/finders/issuable_finder.rb
+++ b/app/finders/issuable_finder.rb
@@ -81,7 +81,8 @@ class IssuableFinder
     elsif current_user && params[:authorized_only].presence && !current_user_related?
       @projects = current_user.authorized_projects.reorder(nil)
     else
-      @projects = ProjectsFinder.new.execute(current_user).reorder(nil)
+      @projects = ProjectsFinder.new.execute(current_user, group: group).
+        reorder(nil)
     end
   end
 
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 2b9bad9c9ea1d04061faaeff1a1055c66192280e..f3a2723ee0d1d4b496aadeeea8570e89889f0509 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -181,10 +181,6 @@ module ApplicationHelper
     end
   end
 
-  def broadcast_message
-    BroadcastMessage.current
-  end
-
   # Render a `time` element with Javascript-based relative date and tooltip
   #
   # time       - Time object
@@ -205,7 +201,7 @@ module ApplicationHelper
   def time_ago_with_tooltip(time, placement: 'top', html_class: 'time_ago', skip_js: false)
     element = content_tag :time, time.to_s,
       class: "#{html_class} js-timeago js-timeago-pending",
-      datetime: time.getutc.iso8601,
+      datetime: time.to_time.getutc.iso8601,
       title: time.in_time_zone.to_s(:medium),
       data: { toggle: 'tooltip', placement: placement, container: 'body' }
 
@@ -266,7 +262,7 @@ module ApplicationHelper
       state: params[:state],
       scope: params[:scope],
       label_name: params[:label_name],
-      milestone_id: params[:milestone_id],
+      milestone_title: params[:milestone_title],
       assignee_id: params[:assignee_id],
       author_id: params[:author_id],
       sort: params[:sort],
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index d31d4cde08f4dcb2579d2fd582c50111401e9bca..7c55934eddaab38e7499627dc4c451d7737dd2a8 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -1,21 +1,10 @@
 module BlobHelper
-  def highlight(blob_name, blob_content, nowrap: false, continue: false)
-    @formatter ||= Rouge::Formatters::HTMLGitlab.new(
-      nowrap: nowrap,
-      cssclass: 'code highlight',
-      lineanchors: true,
-      lineanchorsid: 'LC'
-    )
-
-    begin
-      @lexer ||= Rouge::Lexer.guess(filename: blob_name, source: blob_content).new
-      result = @formatter.format(@lexer.lex(blob_content, continue: continue)).html_safe
-    rescue
-      @lexer = Rouge::Lexers::PlainText
-      result = @formatter.format(@lexer.lex(blob_content)).html_safe
-    end
+  def highlighter(blob_name, blob_content, nowrap: false)
+    Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap)
+  end
 
-    result
+  def highlight(blob_name, blob_content, nowrap: false)
+    Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap)
   end
 
   def no_highlight_files
diff --git a/app/helpers/broadcast_messages_helper.rb b/app/helpers/broadcast_messages_helper.rb
index 6484dca6b5539dd97016fa2403bf87de5b6286a3..1ed8c710f771566dd6ba3b5511911a16704dd93f 100644
--- a/app/helpers/broadcast_messages_helper.rb
+++ b/app/helpers/broadcast_messages_helper.rb
@@ -1,16 +1,34 @@
 module BroadcastMessagesHelper
-  def broadcast_styling(broadcast_message)
-    styling = ''
+  def broadcast_message(message = BroadcastMessage.current)
+    return unless message.present?
+
+    content_tag :div, class: 'broadcast-message', style: broadcast_message_style(message) do
+      icon('bullhorn') << ' ' << message.message
+    end
+  end
+
+  def broadcast_message_style(broadcast_message)
+    style = ''
 
     if broadcast_message.color.present?
-      styling << "background-color: #{broadcast_message.color}"
-      styling << '; ' if broadcast_message.font.present?
+      style << "background-color: #{broadcast_message.color}"
+      style << '; ' if broadcast_message.font.present?
     end
 
     if broadcast_message.font.present?
-      styling << "color: #{broadcast_message.font}"
+      style << "color: #{broadcast_message.font}"
     end
 
-    styling
+    style
+  end
+
+  def broadcast_message_status(broadcast_message)
+    if broadcast_message.active?
+      'Active'
+    elsif broadcast_message.ended?
+      'Expired'
+    else
+      'Pending'
+    end
   end
 end
diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb
index ec0e3f409c1596e03e5f344748ea562ddaca4902..d6c0584374358f6bda94b560208126dec523b0cf 100644
--- a/app/helpers/button_helper.rb
+++ b/app/helpers/button_helper.rb
@@ -17,7 +17,7 @@ module ButtonHelper
   def clipboard_button(data = {})
     content_tag :button,
       icon('clipboard'),
-      class: 'btn btn-xs btn-clipboard',
+      class: 'btn btn-clipboard',
       data: data,
       type: :button
   end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index 24134310fc5d084b4cfa15f1e1c6009b9c51f476..62971d1e14b531e2984e2e83e902aeda3ef8f3ab 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -19,13 +19,13 @@ module DiffHelper
     end
   end
 
-  def safe_diff_files(diffs)
+  def safe_diff_files(diffs, diff_refs)
     lines = 0
     safe_files = []
     diffs.first(allowed_diff_size).each do |diff|
       lines += diff.diff.lines.count
       break if lines > allowed_diff_lines
-      safe_files << Gitlab::Diff::File.new(diff)
+      safe_files << Gitlab::Diff::File.new(diff, diff_refs)
     end
     safe_files
   end
@@ -43,64 +43,6 @@ module DiffHelper
     Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
   end
 
-  def parallel_diff(diff_file, index)
-    lines = []
-    skip_next = false
-
-    # Building array of lines
-    #
-    # [
-    # left_type, left_line_number, left_line_content, left_line_code,
-    # right_line_type, right_line_number, right_line_content, right_line_code
-    # ]
-    #
-    diff_file.diff_lines.each do |line|
-
-      full_line = line.text
-      type = line.type
-      line_code = generate_line_code(diff_file.file_path, line)
-      line_new = line.new_pos
-      line_old = line.old_pos
-
-      next_line = diff_file.next_line(line.index)
-
-      if next_line
-        next_line_code = generate_line_code(diff_file.file_path, next_line)
-        next_type = next_line.type
-        next_line = next_line.text
-      end
-
-      if type == 'match' || type.nil?
-        # line in the right panel is the same as in the left one
-        line = [type, line_old, full_line, line_code, type, line_new, full_line, line_code]
-        lines.push(line)
-      elsif type == 'old'
-        if next_type == 'new'
-          # Left side has text removed, right side has text added
-          line = [type, line_old, full_line, line_code, next_type, line_new, next_line, next_line_code]
-          lines.push(line)
-          skip_next = true
-        elsif next_type == 'old' || next_type.nil?
-          # Left side has text removed, right side doesn't have any change
-          # No next line code, no new line number, no new line text
-          line = [type, line_old, full_line, line_code, next_type, nil, "&nbsp;", nil]
-          lines.push(line)
-        end
-      elsif type == 'new'
-        if skip_next
-          # Change has been already included in previous line so no need to do it again
-          skip_next = false
-          next
-        else
-          # Change is only on the right side, left side has no change
-          line = [nil, nil, "&nbsp;", line_code, type, line_new, full_line, line_code]
-          lines.push(line)
-        end
-      end
-    end
-    lines
-  end
-
   def unfold_bottom_class(bottom)
     (bottom) ? 'js-unfold-bottom' : ''
   end
@@ -111,9 +53,9 @@ module DiffHelper
 
   def diff_line_content(line)
     if line.blank?
-      " &nbsp;"
+      " &nbsp;".html_safe
     else
-      line
+      line.html_safe
     end
   end
 
@@ -160,8 +102,7 @@ module DiffHelper
 
   def commit_for_diff(diff)
     if diff.deleted_file
-      first_commit = @first_commit || @commit
-      first_commit.parent || @first_commit
+      @base_commit || @commit.parent || @commit
     else
       @commit
     end
diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb
index dde83ff36b50b2924682ce306b49c24055af5d58..31bf45baeb708ec4cabf14b60f8e08a10740f68f 100644
--- a/app/helpers/events_helper.rb
+++ b/app/helpers/events_helper.rb
@@ -27,13 +27,15 @@ module EventsHelper
     key = key.to_s
     active = 'active' if @event_filter.active?(key)
     link_opts = {
-      class: "event-filter-link btn btn-default #{active}",
+      class: "event-filter-link",
       id:    "#{key}_event_filter",
       title: "Filter by #{tooltip.downcase}",
     }
 
-    link_to request.path, link_opts do
-      content_tag(:span, ' ' + tooltip)
+    content_tag :li, class: active do
+      link_to request.path, link_opts do
+        content_tag(:span, ' ' + tooltip)
+      end
     end
   end
 
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index ca41657cec152fbdc6f7f90fa29b09cc7a5f6aec..1a226252251beb9c281105da01132fda7dbe1ce7 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -91,7 +91,7 @@ module GitlabMarkdownHelper
   def render_wiki_content(wiki_page)
     case wiki_page.format
     when :markdown
-      markdown(wiki_page.content)
+      markdown(wiki_page.content, pipeline: :wiki, project_wiki: @project_wiki)
     when :asciidoc
       asciidoc(wiki_page.content)
     else
diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb
index a7080ddfefbd3a82572cb897ca1f234569673b62..43262d579e980a32e325571e75746b7e2e893b83 100644
--- a/app/helpers/issues_helper.rb
+++ b/app/helpers/issues_helper.rb
@@ -103,9 +103,12 @@ module IssuesHelper
 
     content_tag :div, "",
       class: "icon emoji-icon emoji-#{unicode}",
-      "data-emoji" => name,
-      "data-aliases" => aliases.join(" "),
-      "data-unicode-name" => unicode
+      title: name,
+      data: {
+        aliases: aliases.join(' '),
+        emoji: name,
+        unicode_name: unicode
+      }
   end
 
   def emoji_author_list(notes, current_user)
diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb
index 5f0c921413a5b8e81ab6c479cbd15ebb2388994f..53c543c28c5d20e9352fb2f105d06f9d606f13dc 100644
--- a/app/helpers/notes_helper.rb
+++ b/app/helpers/notes_helper.rb
@@ -67,7 +67,7 @@ module NotesHelper
       line_type:     line_type
     }
 
-    button_tag class: 'btn reply-btn js-discussion-reply-button',
+    button_tag class: 'btn btn-nr reply-btn js-discussion-reply-button',
                data: data, title: 'Add a reply' do
       link_text = icon('comment')
       link_text << ' Reply'
diff --git a/app/mailers/emails/builds.rb b/app/mailers/emails/builds.rb
index d58609a2de5cca5e61c92754b33562e3735ab683..64c1ce8cfabfb8a0d6632f9a2d6fbc75305dd151 100644
--- a/app/mailers/emails/builds.rb
+++ b/app/mailers/emails/builds.rb
@@ -3,13 +3,26 @@ module Emails
     def build_fail_email(build_id, to)
       @build = Ci::Build.find(build_id)
       @project = @build.project
+      add_project_headers
+      add_build_headers
+      headers['X-GitLab-Build-Status'] = "failed"
       mail(to: to, subject: subject("Build failed for #{@project.name}", @build.short_sha))
     end
 
     def build_success_email(build_id, to)
       @build = Ci::Build.find(build_id)
       @project = @build.project
+      add_project_headers
+      add_build_headers
+      headers['X-GitLab-Build-Status'] = "success"
       mail(to: to, subject: subject("Build success for #{@project.name}", @build.short_sha))
     end
+
+    private
+    def add_build_headers
+      headers['X-GitLab-Build-Id'] = @build.id
+      headers['X-GitLab-Build-Ref'] = @build.ref
+    end
+
   end
 end
diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb
index abdeefed5ef8b05a3d1b529a1e31771adf704fb1..4a88cb61132d26e85dfcb2ef8a3fd3df4c470d79 100644
--- a/app/mailers/emails/issues.rb
+++ b/app/mailers/emails/issues.rb
@@ -1,31 +1,31 @@
 module Emails
   module Issues
     def new_issue_email(recipient_id, issue_id)
-      issue_mail_with_notification(issue_id, recipient_id) do
-        mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
-      end
+      setup_issue_mail(issue_id, recipient_id)
+
+      mail_new_thread(@issue, issue_thread_options(@issue.author_id, recipient_id))
     end
 
     def reassigned_issue_email(recipient_id, issue_id, previous_assignee_id, updated_by_user_id)
-      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
+      setup_issue_mail(issue_id, recipient_id)
+
+      @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
 
     def closed_issue_email(recipient_id, issue_id, updated_by_user_id)
-      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
+      setup_issue_mail(issue_id, recipient_id)
+
+      @updated_by = User.find updated_by_user_id
+      mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id))
     end
 
     def issue_status_changed_email(recipient_id, issue_id, status, updated_by_user_id)
-      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
+      setup_issue_mail(issue_id, recipient_id)
+
+      @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
 
     private
@@ -38,14 +38,12 @@ module Emails
       }
     end
 
-    def issue_mail_with_notification(issue_id, recipient_id)
+    def setup_issue_mail(issue_id, recipient_id)
       @issue = Issue.find(issue_id)
       @project = @issue.project
       @target_url = namespace_project_issue_url(@project.namespace, @project, @issue)
 
-      yield
-
-      SentNotification.record(@issue, recipient_id, reply_key)
+      @sent_notification = SentNotification.record(@issue, recipient_id, reply_key)
     end
   end
 end
diff --git a/app/mailers/emails/merge_requests.rb b/app/mailers/emails/merge_requests.rb
index 7923fb770d0427885b69524fe75cdab46f281703..325996e2e163fa81903c64b557fa9e345dd95514 100644
--- a/app/mailers/emails/merge_requests.rb
+++ b/app/mailers/emails/merge_requests.rb
@@ -1,77 +1,64 @@
 module Emails
   module MergeRequests
     def new_merge_request_email(recipient_id, merge_request_id)
-      @merge_request = MergeRequest.find(merge_request_id)
-      @project = @merge_request.project
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request)
+      setup_merge_request_mail(merge_request_id, recipient_id)
+
       mail_new_thread(@merge_request,
                       from: sender(@merge_request.author_id),
                       to: recipient(recipient_id),
                       subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
-
-      SentNotification.record(@merge_request, recipient_id, reply_key)
     end
 
     def reassigned_merge_request_email(recipient_id, merge_request_id, previous_assignee_id, updated_by_user_id)
-      @merge_request = MergeRequest.find(merge_request_id)
+      setup_merge_request_mail(merge_request_id, recipient_id)
+
       @previous_assignee = User.find_by(id: previous_assignee_id) if previous_assignee_id
-      @project = @merge_request.project
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request)
       mail_answer_thread(@merge_request,
                          from: sender(updated_by_user_id),
                          to: recipient(recipient_id),
                          subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
-
-      SentNotification.record(@merge_request, recipient_id, reply_key)
     end
 
     def closed_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
-      @merge_request = MergeRequest.find(merge_request_id)
+      setup_merge_request_mail(merge_request_id, recipient_id)
+
       @updated_by = User.find updated_by_user_id
-      @project = @merge_request.project
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request)
       mail_answer_thread(@merge_request,
                          from: sender(updated_by_user_id),
                          to: recipient(recipient_id),
                          subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
-
-      SentNotification.record(@merge_request, recipient_id, reply_key)
     end
 
     def merged_merge_request_email(recipient_id, merge_request_id, updated_by_user_id)
-      @merge_request = MergeRequest.find(merge_request_id)
-      @project = @merge_request.project
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request)
+      setup_merge_request_mail(merge_request_id, recipient_id)
+
       mail_answer_thread(@merge_request,
                          from: sender(updated_by_user_id),
                          to: recipient(recipient_id),
                          subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
-
-      SentNotification.record(@merge_request, recipient_id, reply_key)
     end
 
     def merge_request_status_email(recipient_id, merge_request_id, status, updated_by_user_id)
-      @merge_request = MergeRequest.find(merge_request_id)
+      setup_merge_request_mail(merge_request_id, recipient_id)
+
       @mr_status = status
-      @project = @merge_request.project
       @updated_by = User.find updated_by_user_id
-      @target_url = namespace_project_merge_request_url(@project.namespace,
-                                                        @project,
-                                                        @merge_request)
       mail_answer_thread(@merge_request,
                          from: sender(updated_by_user_id),
                          to: recipient(recipient_id),
                          subject: subject("#{@merge_request.title} (##{@merge_request.iid})"))
+    end
+
+    private
+
+    def setup_merge_request_mail(merge_request_id, recipient_id)
+      @merge_request = MergeRequest.find(merge_request_id)
+      @project = @merge_request.project
+      @target_url = namespace_project_merge_request_url(@project.namespace,
+                                                        @project,
+                                                        @merge_request)
 
-      SentNotification.record(@merge_request, recipient_id, reply_key)
+      @sent_notification = SentNotification.record(@merge_request, recipient_id, reply_key)
     end
   end
 end
diff --git a/app/mailers/emails/notes.rb b/app/mailers/emails/notes.rb
index e1382d2da12e158a886ee81b38590f2202e8566a..f9650df9a743726eef6719cba80b5536496c38a7 100644
--- a/app/mailers/emails/notes.rb
+++ b/app/mailers/emails/notes.rb
@@ -1,31 +1,31 @@
 module Emails
   module Notes
     def note_commit_email(recipient_id, note_id)
-      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
+      setup_note_mail(note_id, recipient_id)
+
+      @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
 
     def note_issue_email(recipient_id, note_id)
-      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
+      setup_note_mail(note_id, recipient_id)
+
+      @issue = @note.noteable
+      @target_url = namespace_project_issue_url(*note_target_url_options)
+      mail_answer_thread(@issue, note_thread_options(recipient_id))
     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
+      setup_note_mail(note_id, recipient_id)
+
+      @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
 
     private
@@ -42,13 +42,11 @@ module Emails
       }
     end
 
-    def note_mail_with_notification(note_id, recipient_id)
+    def setup_note_mail(note_id, recipient_id)
       @note = Note.find(note_id)
       @project = @note.project
 
-      yield
-
-      SentNotification.record_note(@note, recipient_id, reply_key)
+      @sent_notification = SentNotification.record_note(@note, recipient_id, reply_key)
     end
   end
 end
diff --git a/app/mailers/emails/projects.rb b/app/mailers/emails/projects.rb
index b96418679bd756ef0a64e7a0b3933b9a6750df3c..377c2999d6c21f62f1ce8287714224dd36cfc594 100644
--- a/app/mailers/emails/projects.rb
+++ b/app/mailers/emails/projects.rb
@@ -43,7 +43,7 @@ module Emails
       @current_user = @created_by = User.find(created_by_id)
       @access_level = access_level
       @invite_email = invite_email
-      
+
       @target_url = namespace_project_url(@project.namespace, @project)
 
       mail(to: @created_by.notification_email,
@@ -65,6 +65,10 @@ module Emails
 
       # used in notify layout
       @target_url = @message.target_url
+      @project = Project.find project_id
+
+      add_project_headers
+      headers['X-GitLab-Author'] = @message.author_username
 
       mail(from:      sender(@message.author_id, @message.send_from_committer_email?),
            reply_to:  @message.reply_to,
diff --git a/app/mailers/notify.rb b/app/mailers/notify.rb
index 3bbdd9cee76c2761718bc65259cbebfd5f8c65bf..8cbc9eefc7b73e6dea9b4dd1160272959d2d38e2 100644
--- a/app/mailers/notify.rb
+++ b/app/mailers/notify.rb
@@ -100,17 +100,11 @@ class Notify < BaseMailer
   end
 
   def mail_thread(model, headers = {})
-    if @project
-      headers['X-GitLab-Project'] = @project.name
-      headers['X-GitLab-Project-Id'] = @project.id
-      headers['X-GitLab-Project-Path'] = @project.path_with_namespace
-    end
-
+    add_project_headers
     headers["X-GitLab-#{model.class.name}-ID"] = model.id
+    headers['X-GitLab-Reply-Key'] = reply_key
 
-    if reply_key
-      headers['X-GitLab-Reply-Key'] = reply_key
-
+    if Gitlab::IncomingEmail.enabled?
       address = Mail::Address.new(Gitlab::IncomingEmail.reply_address(reply_key))
       address.display_name = @project.name_with_namespace
 
@@ -153,4 +147,12 @@ class Notify < BaseMailer
   def reply_key
     @reply_key ||= SentNotification.reply_key
   end
+
+  def add_project_headers
+    return unless @project
+
+    headers['X-GitLab-Project'] = @project.name
+    headers['X-GitLab-Project-Id'] = @project.id
+    headers['X-GitLab-Project-Path'] = @project.path_with_namespace
+  end
 end
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 5a1a67db8e174c63d7d1fc84ef3e1d88b839c50e..ab59a3506a29943cc2dc2644037e7fdcc8adf530 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -160,6 +160,7 @@ class Ability
       @project_report_rules ||= project_guest_rules + [
         :create_commit_status,
         :read_commit_statuses,
+        :read_build_artifacts,
         :download_code,
         :fork_project,
         :create_project_snippet,
@@ -175,7 +176,6 @@ class Ability
         :create_merge_request,
         :create_wiki,
         :manage_builds,
-        :download_build_artifacts,
         :push_code
       ]
     end
diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb
index 55864236b2fd81d162703688f462333db8ebe8e1..cc59aa4e9111ee9790357854a418ee7d141d2ff5 100644
--- a/app/models/abuse_report.rb
+++ b/app/models/abuse_report.rb
@@ -17,7 +17,12 @@ class AbuseReport < ActiveRecord::Base
   validates :reporter, presence: true
   validates :user, presence: true
   validates :message, presence: true
-  validates :user_id, uniqueness: true
+  validates :user_id, uniqueness: { message: 'has already been reported' }
+
+  def remove_user
+    user.block
+    user.destroy
+  end
 
   def notify
     return unless self.persisted?
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 6c6c2468374deb48ce0e3d50c00154f33309a683..2f3487b53ac91ff5de1473ec738d05e33963701d 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -41,6 +41,10 @@
 #  recaptcha_site_key                :string
 #  recaptcha_private_key             :string
 #  metrics_port                      :integer          default(8089)
+#  sentry_enabled                    :boolean          default(FALSE)
+#  sentry_dsn                        :string
+#  ip_blocking_enabled               :boolean          default(FALSE)
+#  dns_blacklist_threshold           :float            default(0.33)
 #
 
 class ApplicationSetting < ActiveRecord::Base
@@ -82,6 +86,10 @@ class ApplicationSetting < ActiveRecord::Base
             presence: true,
             if: :recaptcha_enabled
 
+  validates :sentry_dsn,
+            presence: true,
+            if: :sentry_enabled
+
   validates_each :restricted_visibility_levels do |record, attr, value|
     unless value.nil?
       value.each do |level|
diff --git a/app/models/broadcast_message.rb b/app/models/broadcast_message.rb
index ad514706160bd49b15f20d3226943b738ee11cee..611196337170ad2fb8b12c07f7c90fa697179f26 100644
--- a/app/models/broadcast_message.rb
+++ b/app/models/broadcast_message.rb
@@ -6,7 +6,6 @@
 #  message    :text             not null
 #  starts_at  :datetime
 #  ends_at    :datetime
-#  alert_type :integer
 #  created_at :datetime
 #  updated_at :datetime
 #  color      :string(255)
@@ -23,7 +22,22 @@ class BroadcastMessage < ActiveRecord::Base
   validates :color, allow_blank: true, color: true
   validates :font,  allow_blank: true, color: true
 
+  default_value_for :color, '#E75E40'
+  default_value_for :font,  '#FFFFFF'
+
   def self.current
-    where("ends_at > :now AND starts_at < :now", now: Time.zone.now).last
+    where("ends_at > :now AND starts_at <= :now", now: Time.zone.now).last
+  end
+
+  def active?
+    started? && !ended?
+  end
+
+  def started?
+    Time.zone.now >= starts_at
+  end
+
+  def ended?
+    ends_at < Time.zone.now
   end
 end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index a4779d06de85109877f1d9d792c758e33ac11441..16a5b03f5915c7c28e03f2fcd252257dd97064b3 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -30,10 +30,12 @@
 #  description        :string(255)
 #  artifacts_file     :text
 #  gl_project_id      :integer
+#  artifacts_metadata :text
 #
 
 module Ci
   class Build < CommitStatus
+    include Gitlab::Application.routes.url_helpers
     LAZY_ATTRIBUTES = ['trace']
 
     belongs_to :runner, class_name: 'Ci::Runner'
@@ -49,6 +51,7 @@ module Ci
     scope :similar, ->(build) { where(ref: build.ref, tag: build.tag, trigger_request_id: build.trigger_request_id) }
 
     mount_uploader :artifacts_file, ArtifactUploader
+    mount_uploader :artifacts_metadata, ArtifactUploader
 
     acts_as_taggable
 
@@ -125,6 +128,14 @@ module Ci
       !self.commit.latest_builds_for_ref(self.ref).include?(self)
     end
 
+    def depends_on_builds
+      # Get builds of the same type
+      latest_builds = self.commit.builds.similar(self).latest
+
+      # Return builds from previous stages
+      latest_builds.where('stage_idx < ?', stage_idx)
+    end
+
     def trace_html
       html = Ci::Ansi2html::convert(trace) if trace.present?
       html || ''
@@ -291,21 +302,18 @@ module Ci
     end
 
     def target_url
-      Gitlab::Application.routes.url_helpers.
-        namespace_project_build_url(project.namespace, project, self)
+      namespace_project_build_url(project.namespace, project, self)
     end
 
     def cancel_url
       if active?
-        Gitlab::Application.routes.url_helpers.
-          cancel_namespace_project_build_path(project.namespace, project, self)
+        cancel_namespace_project_build_path(project.namespace, project, self)
       end
     end
 
     def retry_url
       if retryable?
-        Gitlab::Application.routes.url_helpers.
-          retry_namespace_project_build_path(project.namespace, project, self)
+        retry_namespace_project_build_path(project.namespace, project, self)
       end
     end
 
@@ -321,20 +329,35 @@ module Ci
       pending? && !any_runners_online?
     end
 
-    def download_url
-      if artifacts_file.exists?
-        Gitlab::Application.routes.url_helpers.
-          download_namespace_project_build_path(project.namespace, project, self)
-      end
-    end
-
     def execute_hooks
       build_data = Gitlab::BuildDataBuilder.build(self)
       project.execute_hooks(build_data.dup, :build_hooks)
       project.execute_services(build_data.dup, :build_hooks)
     end
 
+    def artifacts?
+      artifacts_file.exists?
+    end
+
+    def artifacts_download_url
+      if artifacts?
+        download_namespace_project_build_artifacts_path(project.namespace, project, self)
+      end
+    end
+
+    def artifacts_browse_url
+      if artifacts_browser_supported?
+        browse_namespace_project_build_artifacts_path(project.namespace, project, self)
+      end
+    end
+
+    def artifacts_browser_supported?
+      artifacts? && artifacts_metadata.exists?
+    end
 
+    def artifacts_metadata_entry(path)
+      Gitlab::Ci::Build::Artifacts::Metadata.new(artifacts_metadata.path, path).to_entry
+    end
 
     private
 
diff --git a/app/models/ci/trigger.rb b/app/models/ci/trigger.rb
index bb98cd5c7dab0a152fda43b3c13edf6949e9cc74..2b9a457c8ab6f32235d5f661056682c14d9089c3 100644
--- a/app/models/ci/trigger.rb
+++ b/app/models/ci/trigger.rb
@@ -33,6 +33,10 @@ module Ci
       trigger_requests.last
     end
 
+    def last_used
+      last_trigger_request.try(:created_at)
+    end
+
     def short_token
       token[0...10]
     end
diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb
index 7f6f497f325dd22723fe7a9e77e4d907e91c9c73..e786bd7dd93c99f9cb4ddc0a588f2c9b0a9d1856 100644
--- a/app/models/ci/variable.rb
+++ b/app/models/ci/variable.rb
@@ -18,8 +18,12 @@ module Ci
     
     belongs_to :project, class_name: '::Project', foreign_key: :gl_project_id
 
-    validates_presence_of :key
     validates_uniqueness_of :key, scope: :gl_project_id
+    validates :key,
+      presence: true,
+      length: { within: 0..255 },
+      format: { with: /\A[a-zA-Z0-9_]+\z/,
+                message: "can contain only letters, digits and '_'." }
 
     attr_encrypted :value, mode: :per_attribute_iv_and_salt, key: Gitlab::Application.secrets.db_key_base
   end
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index ff47949347439de23178b3aaac24929f2dc8c480..66e0502fc0ceabac48f273f56ac85cb77cb47c88 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -56,6 +56,8 @@ class CommitStatus < ActiveRecord::Base
   scope :ordered, -> { order(:ref, :stage_idx, :name) }
   scope :for_ref, ->(ref) { where(ref: ref) }
 
+  AVAILABLE_STATUSES = ['pending', 'running', 'success', 'failed', 'canceled']
+
   state_machine :status, initial: :pending do
     event :run do
       transition pending: :running
@@ -131,7 +133,11 @@ class CommitStatus < ActiveRecord::Base
     false
   end
 
-  def download_url
+  def artifacts_download_url
+    nil
+  end
+
+  def artifacts_browse_url
     nil
   end
 end
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 18a00f95b48291abe09e17d7f9cbe2869988cb19..04650a9e67a14d0374098b0bf7653c8e953f4ac2 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -119,6 +119,12 @@ module Issuable
       update(subscribed: !subscribed?(user))
   end
 
+  def unsubscribe(user)
+    subscriptions.
+      find_or_initialize_by(user_id: user.id).
+      update(subscribed: false)
+  end
+
   def to_hook_data(user)
     {
       object_kind: self.class.name.underscore,
diff --git a/app/models/hooks/project_hook.rb b/app/models/hooks/project_hook.rb
index fa18ba5dbbe031c33f7b41edc3d1f0bfdd9f1b85..fe923fafbe066dcc98d53939d9b317c37f9e553a 100644
--- a/app/models/hooks/project_hook.rb
+++ b/app/models/hooks/project_hook.rb
@@ -3,11 +3,11 @@
 # Table name: web_hooks
 #
 #  id                      :integer          not null, primary key
-#  url                     :string(255)
+#  url                     :string(2000)
 #  project_id              :integer
 #  created_at              :datetime
 #  updated_at              :datetime
-#  type                    :string(255)      default("ProjectHook")
+#  type                    :string           default("ProjectHook")
 #  service_id              :integer
 #  push_events             :boolean          default(TRUE), not null
 #  issues_events           :boolean          default(FALSE), not null
diff --git a/app/models/hooks/service_hook.rb b/app/models/hooks/service_hook.rb
index b333a337347e94e07206a303741cf1bce99af833..80962264ba2c4b398a492521f5ac8dc60c636ba7 100644
--- a/app/models/hooks/service_hook.rb
+++ b/app/models/hooks/service_hook.rb
@@ -3,11 +3,11 @@
 # Table name: web_hooks
 #
 #  id                      :integer          not null, primary key
-#  url                     :string(255)
+#  url                     :string(2000)
 #  project_id              :integer
 #  created_at              :datetime
 #  updated_at              :datetime
-#  type                    :string(255)      default("ProjectHook")
+#  type                    :string           default("ProjectHook")
 #  service_id              :integer
 #  push_events             :boolean          default(TRUE), not null
 #  issues_events           :boolean          default(FALSE), not null
diff --git a/app/models/hooks/system_hook.rb b/app/models/hooks/system_hook.rb
index d81512fae5da43213f7697d47e3b5be102a58d96..c147d8762a91cbbcc1f79e6831955de46d409cde 100644
--- a/app/models/hooks/system_hook.rb
+++ b/app/models/hooks/system_hook.rb
@@ -3,11 +3,11 @@
 # Table name: web_hooks
 #
 #  id                      :integer          not null, primary key
-#  url                     :string(255)
+#  url                     :string(2000)
 #  project_id              :integer
 #  created_at              :datetime
 #  updated_at              :datetime
-#  type                    :string(255)      default("ProjectHook")
+#  type                    :string           default("ProjectHook")
 #  service_id              :integer
 #  push_events             :boolean          default(TRUE), not null
 #  issues_events           :boolean          default(FALSE), not null
diff --git a/app/models/hooks/web_hook.rb b/app/models/hooks/web_hook.rb
index d0aadfc330a1bedcb787cc730dbc2190256faae5..7a13c3f0a3976304fe7f921ea6f0c6e43a7ce8be 100644
--- a/app/models/hooks/web_hook.rb
+++ b/app/models/hooks/web_hook.rb
@@ -3,11 +3,11 @@
 # Table name: web_hooks
 #
 #  id                      :integer          not null, primary key
-#  url                     :string(255)
+#  url                     :string(2000)
 #  project_id              :integer
 #  created_at              :datetime
 #  updated_at              :datetime
-#  type                    :string(255)      default("ProjectHook")
+#  type                    :string           default("ProjectHook")
 #  service_id              :integer
 #  push_events             :boolean          default(TRUE), not null
 #  issues_events           :boolean          default(FALSE), not null
@@ -48,8 +48,8 @@ class WebHook < ActiveRecord::Base
     else
       post_url = url.gsub("#{parsed_url.userinfo}@", "")
       auth = {
-        username: URI.decode(parsed_url.user),
-        password: URI.decode(parsed_url.password),
+        username: CGI.unescape(parsed_url.user),
+        password: CGI.unescape(parsed_url.password),
       }
       response = WebHook.post(post_url,
                               body: data.to_json,
diff --git a/app/models/identity.rb b/app/models/identity.rb
index 8bcdc1949538f9dc67a1de39223f20fc7d68f57f..e1915b079d439d5c954ab04a33f5474700b3aa3d 100644
--- a/app/models/identity.rb
+++ b/app/models/identity.rb
@@ -18,4 +18,8 @@ class Identity < ActiveRecord::Base
   validates :provider, presence: true
   validates :extern_uid, allow_blank: true, uniqueness: { scope: :provider }
   validates :user_id, uniqueness: { scope: :provider }
+
+  def ldap?
+    provider.starts_with?('ldap')
+  end
 end
diff --git a/app/models/issue.rb b/app/models/issue.rb
index f52e47f3e627f7c6c7d3a76f1b1ab363ac6ca3de..7beba9846089b51f6d6263fbc29af6f6a35e97aa 100644
--- a/app/models/issue.rb
+++ b/app/models/issue.rb
@@ -85,10 +85,10 @@ class Issue < ActiveRecord::Base
     reference
   end
 
-  def referenced_merge_requests
+  def referenced_merge_requests(current_user = nil)
     Gitlab::ReferenceExtractor.lazily do
       [self, *notes].flat_map do |note|
-        note.all_references.merge_requests
+        note.all_references(current_user).merge_requests
       end
     end.sort_by(&:iid)
   end
diff --git a/app/models/member.rb b/app/models/member.rb
index 28aee2e379963224b4459c4b5251d10464528d63..34efcd0088d006722958d0ff2c0003bcb4935cf2 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -91,7 +91,7 @@ class Member < ActiveRecord::Base
         member.invite_email = user
       end
 
-      if can_update_member?(current_user, member)
+      if can_update_member?(current_user, member) || project_creator?(member, access_level)
         member.created_by ||= current_user
         member.access_level = access_level
 
@@ -107,6 +107,11 @@ class Member < ActiveRecord::Base
         current_user.can?(:update_group_member, member) ||
         current_user.can?(:update_project_member, member)
     end
+
+    def project_creator?(member, access_level)
+      member.new_record? && member.owner? &&
+        access_level.to_i == ProjectMember::MASTER
+    end
   end
 
   def invite?
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index c63d0c016537099a8ceaed35a26aa9bd64518cc0..41dd248d80a2ad8def8d55336d5145f0415a85f8 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -180,6 +180,14 @@ class MergeRequest < ActiveRecord::Base
     merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
   end
 
+  def diff_base_commit
+    if merge_request_diff
+      merge_request_diff.base_commit
+    else
+      self.target_project.commit(self.target_branch)
+    end
+  end
+
   def last_commit_short_sha
     last_commit.short_id
   end
@@ -254,7 +262,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def mergeable?
-    return false unless open? && !work_in_progress?
+    return false unless open? && !work_in_progress? && !broken?
 
     check_if_can_be_merged
 
@@ -477,8 +485,7 @@ class MergeRequest < ActiveRecord::Base
   end
 
   def target_sha
-    @target_sha ||= target_project.
-      repository.commit(target_branch).sha
+    @target_sha ||= target_project.repository.commit(target_branch).sha
   end
 
   def source_sha
@@ -517,4 +524,10 @@ class MergeRequest < ActiveRecord::Base
   def ci_commit
     @ci_commit ||= source_project.ci_commit(last_commit.id) if last_commit && source_project
   end
+
+  def diff_refs
+    return nil unless diff_base_commit
+
+    [diff_base_commit, last_commit]
+  end
 end
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index c499a4b5b4c8006f834fcf3ffcaee513d89120ec..ba0194cd0a69d7733173b0bea9eb93831ff65469 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -73,6 +73,12 @@ class MergeRequestDiff < ActiveRecord::Base
     commits.last
   end
 
+  def base_commit
+    return nil unless self.base_commit_sha
+
+    merge_request.target_project.commit(self.base_commit_sha)
+  end
+
   def last_commit_short_sha
     @last_commit_short_sha ||= last_commit.short_id
   end
@@ -156,6 +162,9 @@ class MergeRequestDiff < ActiveRecord::Base
     end
 
     self.st_diffs = new_diffs
+
+    self.base_commit_sha = merge_request.target_project.commit(target_branch).try(:sha)
+
     self.save
   end
 
diff --git a/app/models/note.rb b/app/models/note.rb
index 3d5b663c99f0ff545c3682ac422cdcedf9b22e5a..15f48110ad2471a922c050e88668a235a0bc3f30 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -33,7 +33,7 @@ class Note < ActiveRecord::Base
   participant :author
 
   belongs_to :project
-  belongs_to :noteable, polymorphic: true
+  belongs_to :noteable, polymorphic: true, touch: true
   belongs_to :author, class_name: "User"
   belongs_to :updated_by, class_name: "User"
 
@@ -358,6 +358,10 @@ class Note < ActiveRecord::Base
     !system? && !is_award
   end
 
+  def cross_reference_not_visible_for?(user)
+    cross_reference? && referenced_mentionables(user).empty?
+  end
+
   # Checks if note is an award added as a comment
   #
   # If note is an award, this method sets is_award to true
diff --git a/app/models/project.rb b/app/models/project.rb
index 31990485f7d7653e48db6d8bc8cadca024385e00..5579710a47666a98a97e9b49f539b66157fa6088 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -272,6 +272,10 @@ class Project < ActiveRecord::Base
               query: "%#{query.try(:downcase)}%")
     end
 
+    def search_by_visibility(level)
+      where(visibility_level: Gitlab::VisibilityLevel.const_get(level.upcase))
+    end
+
     def search_by_title(query)
       where('projects.archived = ?', false).where('LOWER(projects.name) LIKE :query', query: "%#{query.downcase}%")
     end
@@ -397,7 +401,7 @@ class Project < ActiveRecord::Base
     result.password = '*****' unless result.password.nil?
     result.to_s
   rescue
-    original_url
+    self.import_url
   end
 
   def check_limit
@@ -468,12 +472,9 @@ class Project < ActiveRecord::Base
     !external_issue_tracker
   end
 
-  def external_issues_trackers
-    services.select(&:issue_tracker?).reject(&:default?)
-  end
-
   def external_issue_tracker
-    @external_issues_tracker ||= external_issues_trackers.find(&:activated?)
+    @external_issue_tracker ||=
+      services.issue_trackers.active.without_defaults.first
   end
 
   def can_have_issues_tracker_id?
diff --git a/app/models/project_services/ci_service.rb b/app/models/project_services/ci_service.rb
index c3f70d1f9726272400982a2489125976acbe14c3..e10b5529b4269c370eff512a41c26a7b1980248e 100644
--- a/app/models/project_services/ci_service.rb
+++ b/app/models/project_services/ci_service.rb
@@ -23,14 +23,12 @@
 # List methods you need to implement to get your CI service
 # working with GitLab Merge Requests
 class CiService < Service
-  def category
-    :ci
-  end
-  
+  default_value_for :category, 'ci'
+
   def valid_token?(token)
     self.respond_to?(:token) && self.token.present? && self.token == token
   end
-  
+
   def supported_events
     %w(push)
   end
diff --git a/app/models/project_services/gitlab_issue_tracker_service.rb b/app/models/project_services/gitlab_issue_tracker_service.rb
index 7aa04309f542d55b16cd08c410a7dd8bba9a0f58..05436cd0f792ba91da479aacb4d691b54c2fef46 100644
--- a/app/models/project_services/gitlab_issue_tracker_service.rb
+++ b/app/models/project_services/gitlab_issue_tracker_service.rb
@@ -24,9 +24,7 @@ class GitlabIssueTrackerService < IssueTrackerService
 
   prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url
 
-  def default?
-    true
-  end
+  default_value_for :default, true
 
   def to_param
     'gitlab'
diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb
index 32a81808930312c721733915374298fb94164bc1..0e3fa4a40fe5d0a967b00df49f0c76439c445f18 100644
--- a/app/models/project_services/hipchat_service.rb
+++ b/app/models/project_services/hipchat_service.rb
@@ -120,13 +120,13 @@ class HipchatService < Service
     message << "#{push[:user_name]} "
     if Gitlab::Git.blank_ref?(before)
       message << "pushed new #{ref_type} <a href=\""\
-                 "#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"\
+                 "#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"\
                  " to #{project_link}\n"
     elsif Gitlab::Git.blank_ref?(after)
       message << "removed #{ref_type} <b>#{ref}</b> from <a href=\"#{project.web_url}\">#{project_name}</a> \n"
     else
       message << "pushed to #{ref_type} <a href=\""\
-                  "#{project.web_url}/commits/#{URI.escape(ref)}\">#{ref}</a> "
+                  "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> "
       message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> "
       message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)"
 
@@ -255,8 +255,8 @@ class HipchatService < Service
     status = data[:commit][:status]
     duration = data[:commit][:duration]
 
-    branch_link = "<a href=\"#{project_url}/commits/#{URI.escape(ref)}\">#{ref}</a>"
-    commit_link = "<a href=\"#{project_url}/commit/#{URI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
+    branch_link = "<a href=\"#{project_url}/commits/#{CGI.escape(ref)}\">#{ref}</a>"
+    commit_link = "<a href=\"#{project_url}/commit/#{CGI.escape(sha)}/builds\">#{Commit.truncate_sha(sha)}</a>"
 
     "#{project_link}: Commit #{commit_link} of #{branch_link} #{ref_type} by #{user_name} #{humanized_status(status)} in #{duration} second(s)"
   end
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index bd9b580038fbe5bd7ac1b772119c9f3ae7bd63ee..04c714bfaad4fddee9d29a4fd0ecb4591a26e0af 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -73,9 +73,10 @@ class IrkerService < Service
         'irc[s]://irc.network.net[:port]/#channel. Special cases: if '\
         'you want the channel to be a nickname instead, append ",isnick" to ' \
         'the channel name; if the channel is protected by a secret password, ' \
-        ' append "?key=secretpassword" to the URI. Note that if you specify a ' \
-        ' default IRC URI to prepend before each recipient, you can just give ' \
-        ' a channel name.'  },
+        ' append "?key=secretpassword" to the URI (Note that due to a bug, if you ' \
+        ' want to use a password, you have to omit the "#" on the channel). If you ' \
+        ' specify a default IRC URI to prepend before each recipient, you can just ' \
+        ' give a channel name.'  },
       { type: 'checkbox', name: 'colorize_messages' },
     ]
   end
diff --git a/app/models/project_services/issue_tracker_service.rb b/app/models/project_services/issue_tracker_service.rb
index ed201979d39e461b450f13c52ea8f953b242dfe6..25045224ce5ac2c47b15ee17ca45f4ed6ca47f78 100644
--- a/app/models/project_services/issue_tracker_service.rb
+++ b/app/models/project_services/issue_tracker_service.rb
@@ -23,12 +23,10 @@ class IssueTrackerService < Service
 
   validates :project_url, :issues_url, :new_issue_url, presence: true, if: :activated?
 
-  def category
-    :issue_tracker
-  end
+  default_value_for :category, 'issue_tracker'
 
   def default?
-    false
+    default
   end
 
   def issue_url(iid)
diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb
index b5fec38378b9851200eea31ed1283d03527a4cd9..8ce47495971b97d25225e5a1a42c4d5a3f92f500 100644
--- a/app/models/project_wiki.rb
+++ b/app/models/project_wiki.rb
@@ -38,6 +38,10 @@ class ProjectWiki
     [Gitlab.config.gitlab.url, "/", path_with_namespace, ".git"].join('')
   end
 
+  def wiki_base_path
+    ["/", @project.path_with_namespace, "/wikis"].join('')
+  end
+
   # Returns the Gollum::Wiki object.
   def wiki
     @wiki ||= begin
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index f36eda1531b803cabd4026f54d003d2d3f2710f7..77115597d71a4e90e422e02c87ba44215307339f 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -25,8 +25,6 @@ class SentNotification < ActiveRecord::Base
 
   class << self
     def reply_key
-      return nil unless Gitlab::IncomingEmail.enabled?
-
       SecureRandom.hex(16)
     end
 
@@ -59,11 +57,15 @@ class SentNotification < ActiveRecord::Base
 
     def record_note(note, recipient_id, reply_key, params = {})
       params[:line_code] = note.line_code
-      
+
       record(note.noteable, recipient_id, reply_key, params)
     end
   end
 
+  def unsubscribable?
+    !for_commit?
+  end
+
   def for_commit?
     noteable_type == "Commit"
   end
@@ -75,4 +77,8 @@ class SentNotification < ActiveRecord::Base
       super
     end
   end
+
+  def to_param
+    self.reply_key
+  end
 end
diff --git a/app/models/service.rb b/app/models/service.rb
index 24f4bf7646ee111c5444dc8f9a400f7139b74ca6..721273250ea78d9516149fac720e182ffbdee936 100644
--- a/app/models/service.rb
+++ b/app/models/service.rb
@@ -43,6 +43,9 @@ class Service < ActiveRecord::Base
   validates :project_id, presence: true, unless: Proc.new { |service| service.template? }
 
   scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) }
+  scope :issue_trackers, -> { where(category: 'issue_tracker') }
+  scope :active, -> { where(active: true) }
+  scope :without_defaults, -> { where(default: false) }
 
   scope :push_hooks, -> { where(push_events: true, active: true) }
   scope :tag_push_hooks, -> { where(tag_push_events: true, active: true) }
@@ -51,6 +54,8 @@ class Service < ActiveRecord::Base
   scope :note_hooks, -> { where(note_events: true, active: true) }
   scope :build_hooks, -> { where(build_events: true, active: true) }
 
+  default_value_for :category, 'common'
+
   def activated?
     active
   end
@@ -60,7 +65,7 @@ class Service < ActiveRecord::Base
   end
 
   def category
-    :common
+    read_attribute(:category).to_sym
   end
 
   def initialize_properties
@@ -153,7 +158,7 @@ class Service < ActiveRecord::Base
 
   # Returns a hash of the properties that have been assigned a new value since last save,
   # indicating their original values (attr => original value).
-  # ActiveRecord does not provide a mechanism to track changes in serialized keys, 
+  # ActiveRecord does not provide a mechanism to track changes in serialized keys,
   # so we need a specific implementation for service properties.
   # This allows to track changes to properties set with the accessor methods,
   # but not direct manipulation of properties hash.
@@ -164,7 +169,7 @@ class Service < ActiveRecord::Base
   def reset_updated_properties
     @updated_properties = nil
   end
-  
+
   def async_execute(data)
     return unless supported_events.include?(data[:object_kind])
 
diff --git a/app/models/user.rb b/app/models/user.rb
index 46b36c605b06aa56c8d2b46fb2571edc22537cf4..4214f01f6a46dc166dd85b59a4328b99849f29f2 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -196,10 +196,22 @@ class User < ActiveRecord::Base
   state_machine :state, initial: :active do
     event :block do
       transition active: :blocked
+      transition ldap_blocked: :blocked
+    end
+
+    event :ldap_block do
+      transition active: :ldap_blocked
     end
 
     event :activate do
       transition blocked: :active
+      transition ldap_blocked: :active
+    end
+
+    state :blocked, :ldap_blocked do
+      def blocked?
+        true
+      end
     end
   end
 
@@ -207,7 +219,7 @@ class User < ActiveRecord::Base
 
   # Scopes
   scope :admins, -> { where(admin: true) }
-  scope :blocked, -> { with_state(:blocked) }
+  scope :blocked, -> { with_states(:blocked, :ldap_blocked) }
   scope :active, -> { with_state(:active) }
   scope :not_in_project, ->(project) { project.users.present? ? where("id not in (:ids)", ids: project.users.map(&:id) ) : all }
   scope :without_projects, -> { where('id NOT IN (SELECT DISTINCT(user_id) FROM members)') }
@@ -652,7 +664,10 @@ class User < ActiveRecord::Base
   end
 
   def all_emails
-    [self.email, *self.emails.map(&:email)]
+    all_emails = []
+    all_emails << self.email unless self.temp_oauth_email?
+    all_emails.concat(self.emails.map(&:email))
+    all_emails
   end
 
   def hook_attrs
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb
index e9413c34baed37f1e8008f5c7c8f14f8f8f1c5d7..2a65f0431c4323efac3a4a268df30d9296e8419e 100644
--- a/app/models/wiki_page.rb
+++ b/app/models/wiki_page.rb
@@ -169,7 +169,7 @@ class WikiPage
   private
 
   def set_attributes
-    attributes[:slug] = @page.escaped_url_path
+    attributes[:slug] = @page.url_path
     attributes[:title] = @page.title
     attributes[:format] = @page.format
   end
diff --git a/app/services/create_tag_service.rb b/app/services/create_tag_service.rb
index 2452999382af08dcda44a9d8f0e957e12eb7e399..55985380d31793c17b110fa93bdcc48b7a4ba4e5 100644
--- a/app/services/create_tag_service.rb
+++ b/app/services/create_tag_service.rb
@@ -23,6 +23,7 @@ class CreateTagService < BaseService
       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)
+      CreateCommitBuildsService.new.execute(project, current_user, push_data)
 
       if release_description
         CreateReleaseService.new(@project, @current_user).
diff --git a/app/services/merge_requests/merge_service.rb b/app/services/merge_requests/merge_service.rb
index cabc3d8fabbf95b6b6fc7dd2ae9f57ce4d5ddfda..e8bef250d8b6c45764302ef5a7568b4ac548229f 100644
--- a/app/services/merge_requests/merge_service.rb
+++ b/app/services/merge_requests/merge_service.rb
@@ -44,7 +44,7 @@ module MergeRequests
     def after_merge
       MergeRequests::PostMergeService.new(project, current_user).execute(merge_request)
 
-      if params[:should_remove_source_branch]
+      if params[:should_remove_source_branch].present?
         DeleteBranchService.new(@merge_request.source_project, current_user).
           execute(merge_request.source_branch)
       end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index e4edc55bf696a0a860d5f27a423c9a7baa04b6c1..ca8a41d93b89c3199f7daa530f15f36f708c88f2 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -376,10 +376,10 @@ class NotificationService
   end
 
   def reassign_resource_email(target, project, current_user, method)
-    previous_assignee_id = previous_record(target, "assignee_id")
+    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 = build_recipients(target, project, current_user, action: :reassign, previous_assignee: previous_assignee)
 
     recipients.each do |recipient|
       mailer.send(
@@ -400,22 +400,27 @@ class NotificationService
     end
   end
 
-  def build_recipients(target, project, current_user, extra_recipients = nil)
+  def build_recipients(target, project, current_user, action: nil, previous_assignee: 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)
 
+    # Re-assign is considered as a mention of the new assignee so we add the
+    # new assignee to the list of recipients after we rejected users with
+    # the "on mention" notification level
+    if action == :reassign
+      recipients << previous_assignee if previous_assignee
+      recipients << target.assignee
+    end
+
+    recipients = reject_muted_users(recipients, project)
     recipients = add_subscribed_users(recipients, target)
     recipients = reject_unsubscribed_users(recipients, target)
 
     recipients.delete(current_user)
-    recipients = recipients.uniq
 
-    recipients
+    recipients.uniq
   end
 
   def mailer
diff --git a/app/services/repair_ldap_blocked_user_service.rb b/app/services/repair_ldap_blocked_user_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..863cef7ff61fcd73cd6f5ab4173c0cb8a0cdaf5a
--- /dev/null
+++ b/app/services/repair_ldap_blocked_user_service.rb
@@ -0,0 +1,17 @@
+class RepairLdapBlockedUserService
+  attr_accessor :user
+
+  def initialize(user)
+    @user = user
+  end
+
+  def execute
+    user.block if ldap_hard_blocked?
+  end
+
+  private
+
+  def ldap_hard_blocked?
+    user.ldap_blocked? && !user.ldap_user?
+  end
+end
diff --git a/app/services/system_hooks_service.rb b/app/services/system_hooks_service.rb
index 6dc854ec33d884c76223be6824d0f7e7703aecd1..ea2b26ccb525604f49492fb8977ebec2dab20ac3 100644
--- a/app/services/system_hooks_service.rb
+++ b/app/services/system_hooks_service.rb
@@ -47,7 +47,8 @@ class SystemHooksService
       data.merge!({
         name: model.name,
         email: model.email,
-        user_id: model.id
+        user_id: model.id,
+        username: model.username
       })
     when ProjectMember
       data.merge!(project_member_data(model))
@@ -99,8 +100,10 @@ class SystemHooksService
       project_path: model.project.path,
       project_path_with_namespace: model.project.path_with_namespace,
       project_id: model.project.id,
+      user_username: model.user.username,
       user_name: model.user.name,
       user_email: model.user.email,
+      user_id: model.user.id,
       access_level: model.human_access,
       project_visibility: Project.visibility_levels.key(model.project.visibility_level_field).downcase
     }
@@ -111,6 +114,7 @@ class SystemHooksService
       group_name: model.group.name,
       group_path: model.group.path,
       group_id: model.group.id,
+      user_username: model.user.username,
       user_name: model.user.name,
       user_email: model.user.email,
       user_id: model.user.id,
diff --git a/app/uploaders/artifact_uploader.rb b/app/uploaders/artifact_uploader.rb
index 1b0ae6c00569b56c26c68a64c93203ccd42ae49a..1cd93263c9f42974f91d056231676205b83f5dbe 100644
--- a/app/uploaders/artifact_uploader.rb
+++ b/app/uploaders/artifact_uploader.rb
@@ -32,6 +32,10 @@ class ArtifactUploader < CarrierWave::Uploader::Base
     self.class.storage == CarrierWave::Storage::File
   end
 
+  def filename
+    file.try(:filename)
+  end
+
   def exists?
     file.try(:exists?)
   end
diff --git a/app/views/abuse_reports/new.html.haml b/app/views/abuse_reports/new.html.haml
index 3e5cdd2ce4aa2ee9fd6ef88f31a1cbaaa63ad2af..f125ecf7be5c582839820a8f459d24f97cccb7aa 100644
--- a/app/views/abuse_reports/new.html.haml
+++ b/app/views/abuse_reports/new.html.haml
@@ -16,7 +16,7 @@
   .form-group
     = f.label :message, class: 'control-label'
     .col-sm-10
-      = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
+      = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true, value: sanitize(@ref_url)
       .help-block
         Explain the problem with this user. If appropriate, provide a link to the relevant issue or comment.
 
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index 81337432ab786ccbf6b1911c2425b0829bae091d..c4020c8273b6d43e8d515bf7a7983af7fcc05061 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -202,9 +202,32 @@
         .help-block
           A method call is only tracked when it takes longer to complete than
           the given amount of milliseconds.
+    .form-group
+      = f.label :metrics_sample_interval, 'Sampler Interval (sec)', class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.number_field :metrics_sample_interval, class: 'form-control'
+        .help-block
+          The sampling interval in seconds. Sampled data includes memory usage,
+          retained Ruby objects, file descriptors and so on.
 
   %fieldset
     %legend Spam and Anti-bot Protection
+    .form-group
+      .col-sm-offset-2.col-sm-10
+        .checkbox
+          = f.label :ip_blocking_enabled do
+            = f.check_box :ip_blocking_enabled
+            Enable IP check against blacklist at sign-up
+          .help-block Helps preventing accounts creation from 'known spam sources'
+
+    .form-group
+      = f.label :dnsbl_servers_list, class: 'control-label col-sm-2' do
+        DNSBL servers list
+      .col-sm-10
+        = f.text_field :dnsbl_servers_list, class: 'form-control'
+        .help-block
+          Please enter DNSBL servers separated with comma
+
     .form-group
       .col-sm-offset-2.col-sm-10
         .checkbox
@@ -219,11 +242,30 @@
         = f.text_field :recaptcha_site_key, class: 'form-control'
         .help-block
           Generate site and private keys here:
-          %a{ href: 'http://www.google.com/recaptcha', target: 'blank'} http://www.google.com/recaptcha
+          %a{ href: 'http://www.google.com/recaptcha', target: '_blank'} http://www.google.com/recaptcha
     .form-group
       = f.label :recaptcha_private_key, 'reCAPTCHA Private Key', class: 'control-label col-sm-2'
       .col-sm-10
         = f.text_field :recaptcha_private_key, class: 'form-control'
 
+  %fieldset
+    %legend Error Reporting and Logging
+    %p
+      These settings require a restart to take effect.
+    .form-group
+      .col-sm-offset-2.col-sm-10
+        .checkbox
+          = f.label :sentry_enabled do
+            = f.check_box :sentry_enabled
+            Enable Sentry
+          .help-block
+            Sentry is an error reporting and logging tool which is currently not shipped with GitLab, get it here:
+            %a{ href: 'https://getsentry.com', target: '_blank' } https://getsentry.com
+
+    .form-group
+      = f.label :sentry_dsn, 'Sentry DSN', class: 'control-label col-sm-2'
+      .col-sm-10
+        = f.text_field :sentry_dsn, class: 'form-control'
+
   .form-actions
     = f.submit 'Save', class: 'btn btn-primary'
diff --git a/app/views/admin/broadcast_messages/_form.html.haml b/app/views/admin/broadcast_messages/_form.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..953b8b693684a47a9454ab3c1411b72515f94154
--- /dev/null
+++ b/app/views/admin/broadcast_messages/_form.html.haml
@@ -0,0 +1,37 @@
+.broadcast-message-preview{ style: broadcast_message_style(@broadcast_message) }
+  = icon('bullhorn')
+  %span= @broadcast_message.message || "Your message here"
+
+= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal js-requires-input'} do |f|
+  -if @broadcast_message.errors.any?
+    .alert.alert-danger
+      - @broadcast_message.errors.full_messages.each do |msg|
+        %p= msg
+  .form-group
+    = f.label :message, class: 'control-label'
+    .col-sm-10
+      = f.text_area :message, class: "form-control js-quick-submit", rows: 2, required: true
+  .form-group.js-toggle-colors-container
+    .col-sm-10.col-sm-offset-2
+      = link_to 'Customize colors', '#', class: 'js-toggle-colors-link'
+  .form-group.js-toggle-colors-container.hide
+    = f.label :color, "Background Color", class: 'control-label'
+    .col-sm-10
+      = f.color_field :color, class: "form-control"
+  .form-group.js-toggle-colors-container.hide
+    = f.label :font, "Font Color", class: 'control-label'
+    .col-sm-10
+      = f.color_field :font, class: "form-control"
+  .form-group
+    = f.label :starts_at, class: 'control-label'
+    .col-sm-10.datetime-controls
+      = f.datetime_select :starts_at, {}, class: 'form-control form-control-inline'
+  .form-group
+    = f.label :ends_at, class: 'control-label'
+    .col-sm-10.datetime-controls
+      = f.datetime_select :ends_at, {}, class: 'form-control form-control-inline'
+  .form-actions
+    - if @broadcast_message.persisted?
+      = f.submit "Update broadcast message", class: "btn btn-create"
+    - else
+      = f.submit "Add broadcast message", class: "btn btn-create"
diff --git a/app/views/admin/broadcast_messages/edit.html.haml b/app/views/admin/broadcast_messages/edit.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..45e053eb31d6c3e724b0492aec153c7c198c74c4
--- /dev/null
+++ b/app/views/admin/broadcast_messages/edit.html.haml
@@ -0,0 +1,3 @@
+- page_title "Broadcast Messages"
+
+= render 'form'
diff --git a/app/views/admin/broadcast_messages/index.html.haml b/app/views/admin/broadcast_messages/index.html.haml
index 17dffebd360cae080004c6ba7d3939fadbbd4109..49e33698b635b09b14c5a17236656cb79ecc4a7c 100644
--- a/app/views/admin/broadcast_messages/index.html.haml
+++ b/app/views/admin/broadcast_messages/index.html.haml
@@ -1,60 +1,37 @@
 - page_title "Broadcast Messages"
+
 %h3.page-title
   Broadcast Messages
 %p.light
-  Broadcast messages are displayed for every user and can be used to notify users about scheduled maintenance, recent upgrades and more.
-.broadcast-message-preview
-  %i.fa.fa-bullhorn
-  %span Your message here
-
-= form_for [:admin, @broadcast_message], html: { class: 'broadcast-message-form form-horizontal'} do |f|
-  -if @broadcast_message.errors.any?
-    .alert.alert-danger
-      - @broadcast_message.errors.full_messages.each do |msg|
-        %p= msg
-  .form-group
-    = f.label :message, class: 'control-label'
-    .col-sm-10
-      = f.text_area :message, class: "form-control", rows: 2, required: true
-      %div
-        = link_to '#', class: 'js-toggle-colors-link' do
-          Customize colors
-  .form-group.js-toggle-colors-container.hide
-    = f.label :color, "Background Color", class: 'control-label'
-    .col-sm-10
-      = f.color_field :color, value: "#eb9532", class: "form-control"
-  .form-group.js-toggle-colors-container.hide
-    = f.label :font, "Font Color", class: 'control-label'
-    .col-sm-10
-      = f.color_field :font, value: "#FFFFFF", class: "form-control"
-  .form-group
-    = f.label :starts_at, class: 'control-label'
-    .col-sm-10.datetime-controls
-      = f.datetime_select :starts_at
-  .form-group
-    = f.label :ends_at, class: 'control-label'
-    .col-sm-10.datetime-controls
-      = f.datetime_select :ends_at
-  .form-actions
-    = f.submit "Add broadcast message", class: "btn btn-create"
+  Broadcast messages are displayed for every user and can be used to notify
+  users about scheduled maintenance, recent upgrades and more.
 
--if @broadcast_messages.any?
-  %ul.bordered-list.broadcast-messages
-    - @broadcast_messages.each do |broadcast_message|
-      %li
-        .pull-right
-          - if broadcast_message.starts_at
-            %strong
-              #{broadcast_message.starts_at.to_s(:short)}
-            \...
-          - if broadcast_message.ends_at
-            %strong
-              #{broadcast_message.ends_at.to_s(:short)}
-          &nbsp;
-          = link_to [:admin, broadcast_message], method: :delete, remote: true, class: 'remove-row btn btn-xs' do
-            %i.fa.fa-times.cred
+= render 'form'
 
-        .message= broadcast_message.message
+%br.clearfix
 
+-if @broadcast_messages.any?
+  %table.table
+    %thead
+      %tr
+        %th Status
+        %th Preview
+        %th Starts
+        %th Ends
+        %th &nbsp;
+    %tbody
+      - @broadcast_messages.each do |message|
+        %tr
+          %td
+            = broadcast_message_status(message)
+          %td
+            = broadcast_message(message)
+          %td
+            = message.starts_at
+          %td
+            = message.ends_at
+          %td
+            = link_to icon('pencil-square-o'), edit_admin_broadcast_message_path(message), title: 'Edit', class: 'btn btn-xs'
+            = link_to icon('times'), admin_broadcast_message_path(message), method: :delete, remote: true, title: 'Remove', class: 'js-remove-tr btn btn-xs btn-danger'
 
   = paginate @broadcast_messages
diff --git a/app/views/admin/builds/_build.html.haml b/app/views/admin/builds/_build.html.haml
index 6936e614346a3d9dbee1561c671875fe4310b5bc..c395bd908c334a01d8f8ca65c121cb798d4cf044 100644
--- a/app/views/admin/builds/_build.html.haml
+++ b/app/views/admin/builds/_build.html.haml
@@ -60,8 +60,8 @@
 
   %td
     .pull-right
-      - if current_user && can?(current_user, :download_build_artifacts, project) && build.download_url
-        = link_to build.download_url, title: 'Download artifacts' do
+      - if current_user && can?(current_user, :read_build_artifacts, project) && build.artifacts?
+        = link_to build.artifacts_download_url, title: 'Download artifacts' do
           %i.fa.fa-download
       - if current_user && can?(current_user, :manage_builds, build.project)
         - if build.active?
diff --git a/app/views/admin/builds/index.html.haml b/app/views/admin/builds/index.html.haml
index ddd4e1481ebffd09a23761b03e8401421901ef59..ebf2b7b60e72803ccae482aac1fda763e438362a 100644
--- a/app/views/admin/builds/index.html.haml
+++ b/app/views/admin/builds/index.html.haml
@@ -4,7 +4,7 @@
       - if @all_builds.running_or_pending.any?
         = link_to 'Cancel all', cancel_all_admin_builds_path, data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
 
-  %ul.center-top-menu
+  %ul.nav-links
     %li{class: ('active' if @scope.nil?)}
       = link_to admin_builds_path do
         All
diff --git a/app/views/admin/labels/index.html.haml b/app/views/admin/labels/index.html.haml
index d67454c03e797b2c5f9c9a8e37863456de92e4d1..3c57e3dc174271b1259a54d3f30ecd6361b11f5f 100644
--- a/app/views/admin/labels/index.html.haml
+++ b/app/views/admin/labels/index.html.haml
@@ -1,5 +1,5 @@
 - page_title "Labels"
-= link_to new_admin_label_path, class: "pull-right btn btn-new" do
+= link_to new_admin_label_path, class: "pull-right btn btn-nr btn-new" do
   New label
 %h3.page-title
   Labels
diff --git a/app/views/admin/logs/show.html.haml b/app/views/admin/logs/show.html.haml
index 1484baa78e0aad795d536599e42fe7ef2f90bcd3..af9fdeb073454e205a91d762b4a55d84eb0e6ada 100644
--- a/app/views/admin/logs/show.html.haml
+++ b/app/views/admin/logs/show.html.haml
@@ -1,12 +1,13 @@
 - page_title "Logs"
 - loggers = [Gitlab::GitLogger, Gitlab::AppLogger,
              Gitlab::ProductionLogger, Gitlab::SidekiqLogger]
-%ul.nav.nav-tabs.log-tabs
+%ul.nav-links.log-tabs
   - loggers.each do |klass|
     %li{ class: (klass == Gitlab::GitLogger ? 'active' : '') }
       = link_to klass::file_name, "##{klass::file_name_noext}",
           'data-toggle' => 'tab'
-%p.light To prevent performance issues admin logs output the last 2000 lines
+.gray-content-block
+  To prevent performance issues admin logs output the last 2000 lines
 .tab-content
   - loggers.each do |klass|
     .tab-pane{ class: (klass == Gitlab::GitLogger ? 'active' : ''),
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 0c986af4a958b26529dd44f1c677e5581bcd7d3b..d734e60682a31fa1a1d88c559ce1bdd4d6dfa424 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -1,7 +1,7 @@
 - page_title @project.name_with_namespace, "Projects"
 %h3.page-title
   Project: #{@project.name_with_namespace}
-  = link_to edit_project_path(@project), class: "btn pull-right" do
+  = link_to edit_project_path(@project), class: "btn btn-nr pull-right" do
     %i.fa.fa-pencil-square-o
     Edit
 %hr
diff --git a/app/views/admin/users/_head.html.haml b/app/views/admin/users/_head.html.haml
index 5e17b018163d05f47b86f658b3aa50b0aa7993e2..ce5e21e54cc01b0fb8571154088b25b8056a0a8f 100644
--- a/app/views/admin/users/_head.html.haml
+++ b/app/views/admin/users/_head.html.haml
@@ -7,12 +7,12 @@
 
   .pull-right
     - 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
+      = link_to 'Impersonate', impersonate_admin_user_path(@user), method: :post, class: "btn btn-nr btn-grouped btn-info"
+    = link_to edit_admin_user_path(@user), class: "btn btn-nr btn-grouped" do
       %i.fa.fa-pencil-square-o
       Edit
 %hr
-%ul.nav.nav-tabs
+%ul.nav-links
   = nav_link(path: 'users#show') do
     = link_to "Account", admin_user_path(@user)
   = nav_link(path: 'users#groups') do
@@ -23,3 +23,4 @@
     = link_to "SSH keys", keys_admin_user_path(@user)
   = nav_link(controller: :identities) do
     = link_to "Identities", admin_user_identities_path(@user)
+.append-bottom-default
diff --git a/app/views/admin/users/index.html.haml b/app/views/admin/users/index.html.haml
index a92c9c152b9fd6c65ca4785a3e9e762fcdac47c2..b050a4d01c3312116ec4c560e7510396899d8ab0 100644
--- a/app/views/admin/users/index.html.haml
+++ b/app/views/admin/users/index.html.haml
@@ -1,101 +1,101 @@
 - page_title "Users"
 = render 'shared/show_aside'
 
-.row
-  %aside.col-md-3
-    .admin-filter
-      %ul.nav.nav-pills.nav-stacked
-        %li{class: "#{'active' unless params[:filter]}"}
-          = link_to admin_users_path do
-            Active
-            %small.pull-right= number_with_delimiter(User.active.count)
-        %li{class: "#{'active' if params[:filter] == "admins"}"}
-          = link_to admin_users_path(filter: "admins") do
-            Admins
-            %small.pull-right= number_with_delimiter(User.admins.count)
-        %li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"}
-          = link_to admin_users_path(filter: 'two_factor_enabled') do
-            2FA Enabled
-            %small.pull-right= number_with_delimiter(User.with_two_factor.count)
-        %li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"}
-          = link_to admin_users_path(filter: 'two_factor_disabled') do
-            2FA Disabled
-            %small.pull-right= number_with_delimiter(User.without_two_factor.count)
-        %li{class: "#{'active' if params[:filter] == "blocked"}"}
-          = link_to admin_users_path(filter: "blocked") do
-            Blocked
-            %small.pull-right= number_with_delimiter(User.blocked.count)
-        %li{class: "#{'active' if params[:filter] == "wop"}"}
-          = link_to admin_users_path(filter: "wop") do
-            Without projects
-            %small.pull-right= number_with_delimiter(User.without_projects.count)
-      %hr
-      = form_tag admin_users_path, method: :get, class: 'form-inline' do
-        .form-group
-          = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false
-          = hidden_field_tag "filter", params[:filter]
-        = button_tag class: 'btn btn-primary' do
-          %i.fa.fa-search
-      %hr
-      = link_to 'Reset', admin_users_path, class: "btn btn-cancel"
+.admin-filter
+  %ul.nav-links
+    %li{class: "#{'active' unless params[:filter]}"}
+      = link_to admin_users_path do
+        Active
+        %small.badge= number_with_delimiter(User.active.count)
+    %li{class: "#{'active' if params[:filter] == "admins"}"}
+      = link_to admin_users_path(filter: "admins") do
+        Admins
+        %small.badge= number_with_delimiter(User.admins.count)
+    %li.filter-two-factor-enabled{class: "#{'active' if params[:filter] == 'two_factor_enabled'}"}
+      = link_to admin_users_path(filter: 'two_factor_enabled') do
+        2FA Enabled
+        %small.badge= number_with_delimiter(User.with_two_factor.count)
+    %li.filter-two-factor-disabled{class: "#{'active' if params[:filter] == 'two_factor_disabled'}"}
+      = link_to admin_users_path(filter: 'two_factor_disabled') do
+        2FA Disabled
+        %small.badge= number_with_delimiter(User.without_two_factor.count)
+    %li{class: "#{'active' if params[:filter] == "blocked"}"}
+      = link_to admin_users_path(filter: "blocked") do
+        Blocked
+        %small.badge= number_with_delimiter(User.blocked.count)
+    %li{class: "#{'active' if params[:filter] == "wop"}"}
+      = link_to admin_users_path(filter: "wop") do
+        Without projects
+        %small.badge= number_with_delimiter(User.without_projects.count)
 
-  %section.col-md-9
-    .panel.panel-default
-      .panel-heading
-        Users (#{number_with_delimiter(@users.total_count)})
-        .panel-head-actions
-          .dropdown.inline
-            %a.dropdown-toggle.btn.btn-sm{href: '#', "data-toggle" => "dropdown"}
-              %span.light sort:
-              - if @sort.present?
-                = sort_options_hash[@sort]
-              - else
-                = sort_title_name
-              %b.caret
-            %ul.dropdown-menu
-              %li
-                = link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do
-                  = sort_title_name
-                = link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do
-                  = sort_title_recently_signin
-                = link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do
-                  = sort_title_oldest_signin
-                = link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do
-                  = sort_title_recently_created
-                = link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do
-                  = sort_title_oldest_created
-                = link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do
-                  = sort_title_recently_updated
-                = link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
-                  = sort_title_oldest_updated
-
-          = link_to 'New User', new_admin_user_path, class: "btn btn-new btn-sm"
-      %ul.well-list
-        - @users.each do |user|
+  .gray-content-block.second-block
+    .pull-right
+      .dropdown.inline
+        %a.dropdown-toggle.btn{href: '#', "data-toggle" => "dropdown"}
+          %span.light sort:
+          - if @sort.present?
+            = sort_options_hash[@sort]
+          - else
+            = sort_title_name
+          %b.caret
+        %ul.dropdown-menu
           %li
-            .list-item-name
-              - if user.blocked?
-                %i.fa.fa-lock.cred
+            = link_to admin_users_path(sort: sort_value_name, filter: params[:filter]) do
+              = sort_title_name
+            = link_to admin_users_path(sort: sort_value_recently_signin, filter: params[:filter]) do
+              = sort_title_recently_signin
+            = link_to admin_users_path(sort: sort_value_oldest_signin, filter: params[:filter]) do
+              = sort_title_oldest_signin
+            = link_to admin_users_path(sort: sort_value_recently_created, filter: params[:filter]) do
+              = sort_title_recently_created
+            = link_to admin_users_path(sort: sort_value_oldest_created, filter: params[:filter]) do
+              = sort_title_oldest_created
+            = link_to admin_users_path(sort: sort_value_recently_updated, filter: params[:filter]) do
+              = sort_title_recently_updated
+            = link_to admin_users_path(sort: sort_value_oldest_updated, filter: params[:filter]) do
+              = sort_title_oldest_updated
+
+      = link_to 'New User', new_admin_user_path, class: "btn btn-new"
+    = form_tag admin_users_path, method: :get, class: 'form-inline' do
+      .form-group
+        = search_field_tag :name, params[:name], placeholder: 'Name, email or username', class: 'form-control', spellcheck: false
+        = hidden_field_tag "filter", params[:filter]
+      = button_tag class: 'btn btn-primary' do
+        %i.fa.fa-search
+
+
+.panel.panel-default
+  %ul.well-list
+    - @users.each do |user|
+      %li
+        .list-item-name
+          - if user.blocked?
+            %i.fa.fa-lock.cred
+          - else
+            %i.fa.fa-user.cgreen
+          = link_to user.name, [:admin, user]
+          - if user.admin?
+            %strong.cred (Admin)
+          - if user == current_user
+            %span.cred It's you!
+        .pull-right
+          %span.light
+            %i.fa.fa-envelope
+            = mail_to user.email, user.email, class: 'light'
+          &nbsp;
+          .pull-right
+            = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: 'btn-grouped btn btn-xs'
+            - unless user == current_user
+              - if user.ldap_blocked?
+                = link_to '#', title: 'Cannot unblock LDAP blocked users', data: {toggle: 'tooltip'}, class: 'btn-grouped btn btn-xs btn-success disabled' do
+                  %i.fa.fa-lock
+                  Unblock
+              - elsif user.blocked?
+                = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success'
               - else
-                %i.fa.fa-user.cgreen
-              = link_to user.name, [:admin, user]
-              - if user.admin?
-                %strong.cred (Admin)
-              - if user == current_user
-                %span.cred It's you!
-            .pull-right
-              %span.light
-                %i.fa.fa-envelope
-                = mail_to user.email, user.email, class: 'light'
-              &nbsp;
-              = link_to 'Edit', edit_admin_user_path(user), id: "edit_#{dom_id(user)}", class: "btn btn-xs"
-              - unless user == current_user
-                - if user.blocked?
-                  = link_to 'Unblock', unblock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success"
-                - else
-                  = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: "btn btn-xs btn-warning"
-                - if user.access_locked?
-                  = link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: "btn btn-xs btn-success", data: { confirm: 'Are you sure?' }
-                - if user.can_be_removed?
-                  = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: "btn btn-xs btn-remove"
-    = paginate @users, theme: "gitlab"
+                = link_to 'Block', block_admin_user_path(user), data: {confirm: 'USER WILL BE BLOCKED! Are you sure?'}, method: :put, class: 'btn-grouped btn btn-xs btn-warning'
+              - if user.access_locked?
+                = link_to 'Unlock', unlock_admin_user_path(user), method: :put, class: 'btn-grouped btn btn-xs btn-success', data: { confirm: 'Are you sure?' }
+              - if user.can_be_removed?
+                = link_to 'Destroy', [:admin, user], data: { confirm: "USER #{user.name} WILL BE REMOVED! All issues, merge requests and groups linked to this user will also be removed! Maybe block the user instead? Are you sure?" }, method: :delete, class: 'btn-grouped btn btn-xs btn-remove'
+= paginate @users, theme: "gitlab"
diff --git a/app/views/admin/users/show.html.haml b/app/views/admin/users/show.html.haml
index 2c2450d4117ae3cc76a677dc72c16634296fb999..2bdbae195880edfd7a83c81d57268d24fcd6403a 100644
--- a/app/views/admin/users/show.html.haml
+++ b/app/views/admin/users/show.html.haml
@@ -70,6 +70,14 @@
             %strong.cred
               No
 
+        %li
+          %span.light Current sign-in IP:
+          %strong
+            - if @user.current_sign_in_ip
+              = @user.current_sign_in_ip
+            - else
+              never
+
         %li
           %span.light Current sign-in at:
           %strong
@@ -78,6 +86,14 @@
             - else
               never
 
+        %li
+          %span.light Last sign-in IP:
+          %strong
+            - if @user.last_sign_in_ip
+              = @user.last_sign_in_ip
+            - else
+              never
+
         %li
           %span.light Last sign-in at:
           %strong
diff --git a/app/views/ci/lints/show.html.haml b/app/views/ci/lints/show.html.haml
index a144c43be477a260ccdad87422f114634285f135..0044d779c316728fabe30495b6dfb74c0a590038 100644
--- a/app/views/ci/lints/show.html.haml
+++ b/app/views/ci/lints/show.html.haml
@@ -4,12 +4,12 @@
 .row
   = form_tag ci_lint_path, method: :post do
     .form-group
-      = label_tag :content, 'Content of .gitlab-ci.yml', class: 'control-label text-nowrap'
+      = label_tag(:content, 'Content of .gitlab-ci.yml', class: 'control-label text-nowrap')
       .col-sm-12
-        = text_area_tag :content, nil, class: 'form-control span1', rows: 7, require: true
+        = text_area_tag(:content, @content, class: 'form-control span1', rows: 7, require: true)
     .col-sm-12
       .pull-left.prepend-top-10
-        = submit_tag 'Validate', class: 'btn btn-success submit-yml'
+        = submit_tag('Validate', class: 'btn btn-success submit-yml')
 
 .row.prepend-top-20
   .col-sm-12
diff --git a/app/views/dashboard/_activities.html.haml b/app/views/dashboard/_activities.html.haml
index f98fd9f06ba707c49ae8325da944e1a54198c500..dc76599b7767fe4ded0b42c8c80e2292de72b7b1 100644
--- a/app/views/dashboard/_activities.html.haml
+++ b/app/views/dashboard/_activities.html.haml
@@ -1,9 +1,9 @@
 .hidden-xs
   = render "events/event_last_push", event: @last_push
 
-.gray-content-block
+.nav-block
   - if current_user
-    .pull-right
+    .controls
       = link_to dashboard_projects_path(:atom, { private_token: current_user.private_token }), class: 'btn rss-btn' do
         %i.fa.fa-rss
   = render 'shared/event_filter'
diff --git a/app/views/dashboard/_activity_head.html.haml b/app/views/dashboard/_activity_head.html.haml
index 9f4be025bf2050c67d7724c140e214ef559bf411..b78e70ebc1e936554adaf03c090b3ddb0fd65e85 100644
--- a/app/views/dashboard/_activity_head.html.haml
+++ b/app/views/dashboard/_activity_head.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu
+%ul.nav-links
   %li{ class: ("active" unless params[:filter]) }
     = link_to activity_dashboard_path, class: 'shortcuts-activity', data: {placement: 'right'} do
       Your Projects
diff --git a/app/views/dashboard/_groups_head.html.haml b/app/views/dashboard/_groups_head.html.haml
index 64bd356f5464fb280838208af9e549fc3cc4c9cc..6ca97a692b412569db977471d77063a3ec8e47ac 100644
--- a/app/views/dashboard/_groups_head.html.haml
+++ b/app/views/dashboard/_groups_head.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu
+%ul.nav-links
   = nav_link(page: dashboard_groups_path) do
     = link_to dashboard_groups_path, title: 'Your groups', data: {placement: 'right'} do
       Your Groups
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index f4a3e3162bf5da1eeaecc7d4b6b56f290a94f427..5c4b58cd6887368269f5801fa2aff6b2a01be47b 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -1,7 +1,7 @@
 = content_for :flash_message do
   = render 'shared/project_limit'
 .top-area
-  %ul.left-top-menu
+  %ul.nav-links
     = nav_link(page: [dashboard_projects_path, root_path]) do
       = link_to dashboard_projects_path, title: 'Home', class: 'shortcuts-activity', data: {placement: 'right'} do
         Your Projects
diff --git a/app/views/dashboard/_snippets_head.html.haml b/app/views/dashboard/_snippets_head.html.haml
index 0ae62d6f1b6622dde84f5ffb75a58b94335e9d8d..b25e8ea1f0cdfb30cef06192024684e9508589a4 100644
--- a/app/views/dashboard/_snippets_head.html.haml
+++ b/app/views/dashboard/_snippets_head.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu
+%ul.nav-links
   = nav_link(page: dashboard_snippets_path, html_options: {class: 'home'}) do
     = link_to dashboard_snippets_path, title: 'Your snippets', data: {placement: 'right'} do
       Your Snippets
diff --git a/app/views/dashboard/milestones/show.html.haml b/app/views/dashboard/milestones/show.html.haml
index 4316c358dcb81c2aa0f87e490b95499d529b8156..3810267577c1a0cd0e0677cf18300252a3960ffc 100644
--- a/app/views/dashboard/milestones/show.html.haml
+++ b/app/views/dashboard/milestones/show.html.haml
@@ -16,7 +16,7 @@
 
 - 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.
+    %span All issues for this milestone are closed. Navigate to the project to close the milestone.
 
 .table-holder
   %table.table
@@ -48,7 +48,7 @@
     #{@milestone.open_items_count} open
   = milestone_progress_bar(@milestone)
 
-%ul.center-top-menu.no-top.no-bottom
+%ul.nav-links.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
diff --git a/app/views/dashboard/snippets/index.html.haml b/app/views/dashboard/snippets/index.html.haml
index 07b6d57932ea70a3bef91bcc2a52131ed8899812..d4e7862981c2a3b9190e940d0c10544e205e639f 100644
--- a/app/views/dashboard/snippets/index.html.haml
+++ b/app/views/dashboard/snippets/index.html.haml
@@ -3,32 +3,36 @@
 
 = render 'dashboard/snippets_head'
 
-.gray-content-block
-  .pull-right
+.nav-block
+  .controls
     = link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
       = icon('plus')
       New Snippet
 
-  .btn-group.btn-group-next.snippet-scope-menu
-    = link_to dashboard_snippets_path, class: "btn btn-default #{"active" unless params[:scope]}" do
-      All
-      %span.badge
-        = current_user.snippets.count
-
-    = link_to dashboard_snippets_path(scope: 'are_private'), class: "btn btn-default #{"active" if params[:scope] == "are_private"}" do
-      Private
-      %span.badge
-        = current_user.snippets.are_private.count
-
-    = link_to dashboard_snippets_path(scope: 'are_internal'), class: "btn btn-default #{"active" if params[:scope] == "are_internal"}" do
-      Internal
-      %span.badge
-        = current_user.snippets.are_internal.count
-
-    = link_to dashboard_snippets_path(scope: 'are_public'), class: "btn btn-default #{"active" if params[:scope] == "are_public"}" do
-      Public
-      %span.badge
-        = current_user.snippets.are_public.count
+  .nav-links.snippet-scope-menu
+    %li{ class: ("active" unless params[:scope]) }
+      = link_to dashboard_snippets_path do
+        All
+        %span.badge
+          = current_user.snippets.count
+
+    %li{ class: ("active" if params[:scope] == "are_private") }
+      = link_to dashboard_snippets_path(scope: 'are_private') do
+        Private
+        %span.badge
+          = current_user.snippets.are_private.count
+
+    %li{ class: ("active" if params[:scope] == "are_internal") }
+      = link_to dashboard_snippets_path(scope: 'are_internal') do
+        Internal
+        %span.badge
+          = current_user.snippets.are_internal.count
+
+    %li{ class: ("active" if params[:scope] == "are_public") }
+      = link_to dashboard_snippets_path(scope: 'are_public') do
+        Public
+        %span.badge
+          = current_user.snippets.are_public.count
 
 = render 'snippets/snippets'
 
diff --git a/app/views/devise/shared/_signin_box.html.haml b/app/views/devise/shared/_signin_box.html.haml
index 41ad2c231d4d1cfaa0c2d3478d2e185ca579dcbb..2c15e2c4891c0ab90e6b961a66deef3df24c143b 100644
--- a/app/views/devise/shared/_signin_box.html.haml
+++ b/app/views/devise/shared/_signin_box.html.haml
@@ -7,7 +7,7 @@
       %h3 Sign in
   .login-body
     - if form_based_providers.any?
-      %ul.nav.nav-tabs
+      %ul.nav-links
         - if crowd_enabled?
           %li.active
             = link_to "Crowd", "#tab-crowd", 'data-toggle' => 'tab'
diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml
index 7e3e2e28bc9d17600fc11094b6ee07267363d7f4..e2f97fd933756e15f8ac0f5d1e4f937cb6f2fac7 100644
--- a/app/views/groups/edit.html.haml
+++ b/app/views/groups/edit.html.haml
@@ -1,7 +1,7 @@
 - header_title group_title(@group, "Settings", edit_group_path(@group))
 - @blank_container = true
 
-.panel.panel-default
+.panel.panel-default.prepend-top-default
   .panel-heading
     Group settings
   .panel-body
diff --git a/app/views/groups/group_members/_new_group_member.html.haml b/app/views/groups/group_members/_new_group_member.html.haml
index 3361d7e2a8d279fb6ffaed64de52c00615f74c2d..e7ab4f2409bb941ffb9bd020891200fb7496e53f 100644
--- a/app/views/groups/group_members/_new_group_member.html.haml
+++ b/app/views/groups/group_members/_new_group_member.html.haml
@@ -4,7 +4,7 @@
     .col-sm-10
       = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
       .help-block
-        Search for existing users or invite new ones using their email address.
+        Search for users by name, username, or email, or invite new ones using their email address.
 
   .form-group
     = f.label :access_level, "Group Access", class: 'control-label'
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index 335bf036074367eb98b460408a7e231a2fb1fa2f..6a8acc42af9cac872ea76d8f0e9852aab62bab1f 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -2,7 +2,7 @@
 - header_title group_title(@group, "Members", group_group_members_path(@group))
 - @blank_container = true
 
-.group-members-page
+.group-members-page.prepend-top-default
   - if current_user && current_user.can?(:admin_group_member, @group)
     .panel.panel-default
       .panel-heading
diff --git a/app/views/groups/milestones/show.html.haml b/app/views/groups/milestones/show.html.haml
index d063b257b5e6df7aae5f9d79fdbdfc165718f228..1233da8552492f5da6d52ed5eaa1b0394b93c043 100644
--- a/app/views/groups/milestones/show.html.haml
+++ b/app/views/groups/milestones/show.html.haml
@@ -54,7 +54,7 @@
     #{@milestone.open_items_count} open
   = milestone_progress_bar(@milestone)
 
-%ul.center-top-menu.no-top.no-bottom
+%ul.nav-links.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml
index f1d507a50c7409b9ef42045d15b03a1a8c14376c..9ca11ed117738f97ea968a0e6d6aed393cbd6448 100644
--- a/app/views/groups/projects.html.haml
+++ b/app/views/groups/projects.html.haml
@@ -1,7 +1,7 @@
 - page_title "Projects"
 - header_title group_title(@group, "Projects", projects_group_path(@group))
 
-.panel.panel-default
+.panel.panel-default.prepend-top-default
   .panel-heading
     %strong= @group.name
     projects:
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 48a544fc834797600dd8d6c6853dcfcdbc53a4cf..ebb3df7dca3881b9fe7435926f8c44cfa5d43a36 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -1,3 +1,5 @@
+- @no_container = true
+
 - unless can?(current_user, :read_group, @group)
   - @disable_search_panel = true
 
@@ -25,8 +27,8 @@
     .cover-desc.description
       = markdown(@group.description, pipeline: :description)
 
-- if can?(current_user, :read_group, @group)
-  %ul.center-top-menu.no-top
+
+  %ul.nav-links
     %li.active
       = link_to "#activity", 'data-toggle' => 'tab' do
         Activity
@@ -35,20 +37,22 @@
         = link_to "#projects", 'data-toggle' => 'tab' do
           Projects
 
-  .tab-content
-    .tab-pane.active#activity
-      .gray-content-block.activity-filter-block
-        - if current_user
-          = render "events/event_last_push", event: @last_push
+- if can?(current_user, :read_group, @group)
+  %div{ class: container_class }
+    .tab-content
+      .tab-pane.active#activity
+        .activity-filter-block
+          - if current_user
+            = render "events/event_last_push", event: @last_push
 
-          = render 'shared/event_filter'
+            = render 'shared/event_filter'
 
-      .content_list
-      = spinner
+        .content_list
+        = spinner
 
-    .tab-pane#projects
-      = render "projects", projects: @projects
+      .tab-pane#projects
+        = render "projects", projects: @projects
 
 - else
-  %p.center-top-menu.no-top
+  %p.nav-links.no-top
     No projects to show
diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml
index d9ffda884c8c56663721fa1f568365b7d293829e..7b45bd09050d0abfacf8940acfeba3a51688f326 100644
--- a/app/views/help/ui.html.haml
+++ b/app/views/help/ui.html.haml
@@ -139,26 +139,9 @@
   %h2#navs Navigation
 
   %h4
-    %code .center-top-menu
+    %code .nav-links
   .example
-    %ul.center-top-menu
-      %li.active
-        %a Open
-      %li
-        %a Closed
-
-  %h4
-    %code .btn-group.btn-group-next
-  .example
-    %div.btn-group.btn-group-next
-      %a.btn.active Open
-      %a.btn Closed
-
-
-  %h4
-    %code .nav.nav-tabs
-  .example
-    %ul.nav.nav-tabs
+    %ul.nav-links
       %li.active
         %a Open
       %li
diff --git a/app/views/kaminari/gitlab/_next_page.html.haml b/app/views/kaminari/gitlab/_next_page.html.haml
index 00c5f0b6f4e3fc9a71c81d2a674fcaac213c856d..c805914fc3fcc03033c27734eefa49c2173b711f 100644
--- a/app/views/kaminari/gitlab/_next_page.html.haml
+++ b/app/views/kaminari/gitlab/_next_page.html.haml
@@ -5,5 +5,9 @@
 -#    num_pages:     total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
-%li.next
-  = link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote
+- if current_page.last?
+  %li{ class: "next disabled" }
+    %span= raw(t 'views.pagination.next')
+- else
+  %li{ class: "next" }
+    = link_to raw(t 'views.pagination.next'), url, rel: 'next', remote: remote
diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml
index 2f645186921885198c412560842cf6eeffa00844..a12c53bcfe713401ebcf6d1e026f5edd59cf6917 100644
--- a/app/views/kaminari/gitlab/_paginator.html.haml
+++ b/app/views/kaminari/gitlab/_paginator.html.haml
@@ -10,13 +10,13 @@
     %ul.pagination.clearfix
       - unless current_page.first?
         = first_page_tag unless num_pages < 5 # As kaminari will always show the first 5 pages
-        = prev_page_tag
+      = prev_page_tag
       - each_page do |page|
         - if page.left_outer? || page.right_outer? || page.inside_window?
           = page_tag page
         - elsif !page.was_truncated?
           = gap_tag
+      = next_page_tag
       - unless current_page.last?
-        = next_page_tag
         = last_page_tag unless num_pages < 5
 
diff --git a/app/views/kaminari/gitlab/_prev_page.html.haml b/app/views/kaminari/gitlab/_prev_page.html.haml
index f673abdb3ae8b6cc9859f1ede9d24a44e431c551..afb20455e0a3727f2fb70f7fed0b489a9af03f43 100644
--- a/app/views/kaminari/gitlab/_prev_page.html.haml
+++ b/app/views/kaminari/gitlab/_prev_page.html.haml
@@ -5,5 +5,9 @@
 -#    num_pages:     total number of pages
 -#    per_page:      number of items to fetch per page
 -#    remote:        data-remote
-%li{class: "prev" }
-  = link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote
+- if current_page.first?
+  %li{ class: "prev disabled" }
+    %span= raw(t 'views.pagination.previous')
+- else
+  %li{ class: "prev" }
+    = link_to raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote
diff --git a/app/views/layouts/_broadcast.html.haml b/app/views/layouts/_broadcast.html.haml
index e7d477c225eb35e812b6a592510b862f7e949eee..3a7e0929c167f5581ba41785cdc65aac86841640 100644
--- a/app/views/layouts/_broadcast.html.haml
+++ b/app/views/layouts/_broadcast.html.haml
@@ -1,4 +1 @@
-- if broadcast_message.present?
-  .broadcast-message{ style: broadcast_styling(broadcast_message) }
-    %i.fa.fa-bullhorn
-    = broadcast_message.message
+= broadcast_message
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index ec7cd79bc542c60026c3265a95146d9715838717..26159989777a81ad2a912094dded527583bb556e 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -24,7 +24,7 @@
   .content-wrapper
     = render "layouts/flash"
     = yield :flash_message
-    %div{ class: container_class }
+    %div{ class: (container_class unless @no_container) }
       .content
         .clearfix
           = yield
diff --git a/app/views/layouts/header/_default.html.haml b/app/views/layouts/header/_default.html.haml
index 3892ef8eefa026bb8f2332a0ff99f074d620db85..fcb6b835a7e07ebdc0036a7d455f15179f44d6f6 100644
--- a/app/views/layouts/header/_default.html.haml
+++ b/app/views/layouts/header/_default.html.haml
@@ -37,3 +37,6 @@
       %h1.title= title
 
 = render 'shared/outdated_browser'
+- if @project && !@project.empty_repo?
+  :javascript
+    var findFileURL = "#{namespace_project_find_file_path(@project.namespace, @project, @ref || @project.repository.root_ref)}";
\ No newline at end of file
diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml
index 3ca4c340406511d7488e76997834f7dd75e0aa64..325c68c69dcae363039e0b877b08857ba8751680 100644
--- a/app/views/layouts/notify.html.haml
+++ b/app/views/layouts/notify.html.haml
@@ -44,6 +44,10 @@
           %br
           -# Don't link the host is the line below, one link in the email is easier to quickly click than two.
           You're receiving this email because of your account on #{Gitlab.config.gitlab.host}.
-          If you'd like to receive fewer emails, you can adjust your notification settings.
+          If you'd like to receive fewer emails, you can
+          - if @sent_notification && @sent_notification.unsubscribable?
+            = link_to "unsubscribe", unsubscribe_sent_notification_url(@sent_notification)
+            from this thread or
+          adjust your notification settings.
 
-          = email_action @target_url
\ No newline at end of file
+          = email_action @target_url
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 17e47c622ceec6d1645f6cda4cd60f3e926d1519..a42fd38de3a91b6a458318c2059bf537871a0a0a 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -6,7 +6,7 @@
   .alert.alert-info
     Some options are unavailable for LDAP accounts
 
-.account-page
+.account-page.prepend-top-default
   .panel.panel-default.update-token
     .panel-heading
       Reset Private token
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index 9459d8a6295951bf00f446e302c1895a9613f45e..add9a00138b3f0f0a58d67166281eeb30c0695ce 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -21,10 +21,10 @@
       .form-group
         = f.label :email, class: "control-label"
         .col-sm-10
-          - if @user.ldap_user?
+          - if @user.ldap_user? && @user.ldap_email?
             = f.text_field :email, class: "form-control", required: true, readonly: true
             %span.help-block.light
-              Email is read-only for LDAP user
+              Your email address was automatically set based on the LDAP server.
           - else
             - if @user.temp_oauth_email?
               = f.text_field :email, class: "form-control", required: true, value: nil
diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml
index 101880bd1053451877c8d31521562dd63a5a6ffa..961b61d2e763d02264648b299f3f96bfdb42a7bd 100644
--- a/app/views/projects/_activity.html.haml
+++ b/app/views/projects/_activity.html.haml
@@ -1,6 +1,6 @@
-.gray-content-block.activity-filter-block
+.nav-block.activity-filter-block
   - if current_user
-    .pull-right
+    .controls
       = link_to namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "Feed", class: 'btn rss-btn' do
         %i.fa.fa-rss
 
diff --git a/app/views/projects/_files.html.haml b/app/views/projects/_files.html.haml
index fa978325ddd5923f65020d669bb1418041fa3d86..96c2fa87f45d49e10efa952d6f6513fb4992898c 100644
--- a/app/views/projects/_files.html.haml
+++ b/app/views/projects/_files.html.haml
@@ -1,5 +1,5 @@
 #tree-holder.tree-holder.clearfix
-  .gray-content-block.second-block
+  .nav-block
     = render 'projects/tree/tree_header', tree: @tree
 
   = render 'projects/tree/tree_content', tree: @tree
diff --git a/app/views/projects/_home_panel.html.haml b/app/views/projects/_home_panel.html.haml
index 53eec76129b3bc5f6d4c839a28259fcce3852bcf..298c66649975879648a8698de29362a7140864fd 100644
--- a/app/views/projects/_home_panel.html.haml
+++ b/app/views/projects/_home_panel.html.haml
@@ -44,13 +44,16 @@
       = render 'projects/buttons/star'
       = render 'projects/buttons/fork'
 
-    = render "shared/clone_panel"
+    .clone-row
+      .project-clone-holder
+        = render "shared/clone_panel"
 
-    .split-repo-buttons
-      = render "projects/buttons/download"
-      = render 'projects/buttons/dropdown'
+      .split-repo-buttons
+        .btn-group.pull-left
+          = render "projects/buttons/download"
+          = render 'projects/buttons/dropdown'
 
-    = render 'projects/buttons/notifications'
+      = render 'projects/buttons/notifications'
 
 :javascript
   new Star();
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 54c818baaf41168f2673bda0a3b9961c8d9f514e..1fb37ef6621ddf0e497c6d1260958981f843c6f5 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -1,6 +1,6 @@
 .md-area
   .md-header.clearfix
-    %ul.center-top-menu
+    %ul.nav-links
       %li.active
         %a.js-md-write-button(href="#md-write-holder" tabindex="-1")
           Write
diff --git a/app/views/projects/_zen.html.haml b/app/views/projects/_zen.html.haml
index d582956827555a48d5465f0f872ec745c4a2c54a..e701253d7ded5f461067e47aa80ad2488a3df272 100644
--- a/app/views/projects/_zen.html.haml
+++ b/app/views/projects/_zen.html.haml
@@ -1,6 +1,6 @@
 .zennable
   .zen-backdrop
-    - classes << ' js-gfm-input markdown-area'
+    - classes << ' js-gfm-input js-autosize markdown-area'
     - if defined?(f) && f
       = f.text_area attr, class: classes
     - else
diff --git a/app/views/projects/artifacts/_tree_directory.html.haml b/app/views/projects/artifacts/_tree_directory.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..def493c56f5148000522770ece1ced9dcf76368f
--- /dev/null
+++ b/app/views/projects/artifacts/_tree_directory.html.haml
@@ -0,0 +1,8 @@
+- path_to_directory = browse_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: directory.path)
+
+%tr.tree-item{ 'data-link' => path_to_directory}
+  %td.tree-item-file-name
+    = tree_icon('folder', '755', directory.name)
+    %span.str-truncated
+      = link_to directory.name, path_to_directory
+  %td
diff --git a/app/views/projects/artifacts/_tree_file.html.haml b/app/views/projects/artifacts/_tree_file.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..36fb4c998c96bac066dba1a43dbcc94b968b7379
--- /dev/null
+++ b/app/views/projects/artifacts/_tree_file.html.haml
@@ -0,0 +1,9 @@
+- path_to_file = file_namespace_project_build_artifacts_path(@project.namespace, @project, @build, path: file.path)
+
+%tr.tree-item{ 'data-link' => path_to_file }
+  %td.tree-item-file-name
+    = tree_icon('file', '664', file.name)
+    %span.str-truncated
+      = link_to file.name, path_to_file
+  %td
+    = number_to_human_size(file.metadata[:size], precision: 2)
diff --git a/app/views/projects/artifacts/browse.html.haml b/app/views/projects/artifacts/browse.html.haml
new file mode 100644
index 0000000000000000000000000000000000000000..84034c8bf16e952e173235f1b991950014d7e97e
--- /dev/null
+++ b/app/views/projects/artifacts/browse.html.haml
@@ -0,0 +1,22 @@
+- page_title 'Artifacts', "#{@build.name} (##{@build.id})", 'Builds'
+= render 'projects/builds/header_title'
+
+.top-block.gray-content-block.clearfix
+  .pull-right
+    = link_to download_namespace_project_build_artifacts_path(@project.namespace, @project, @build),
+      class: 'btn btn-default download' do
+      = icon('download')
+      Download artifacts archive
+
+.tree-holder
+  %div.tree-content-holder
+    %table.table.tree-table
+      %thead
+        %tr
+          %th Name
+          %th Size
+      = render partial: 'tree_directory', collection: @entry.directories(parent: true), as: :directory
+      = render partial: 'tree_file', collection: @entry.files, as: :file
+
+- if @entry.empty?
+  .center Empty
diff --git a/app/views/projects/blame/show.html.haml b/app/views/projects/blame/show.html.haml
index 8d9ec068a4358a38ba030fb9665033a67d9ea643..d5d04954490cae82bf8e6dbd49f0ee32ff5cd798 100644
--- a/app/views/projects/blame/show.html.haml
+++ b/app/views/projects/blame/show.html.haml
@@ -15,6 +15,7 @@
     .file-content.blame.highlight
       %table
         - current_line = 1
+        - blame_highlighter = highlighter(@blob.name, @blob.data, nowrap: true)
         - @blame.each do |blame_group|
           %tr
             %td.blame-commit
@@ -41,5 +42,5 @@
               %pre{class: 'code highlight white'}
                 %code
                   - blame_group[:lines].each do |line|
-                    :erb
-                      <%= highlight(@blob.name, line, nowrap: true, continue: true).html_safe %>
+                    :preserve
+                      #{blame_highlighter.highlight(line)}
diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml
index 2a3315da3dbffa54200168ea6b7801a8a316860e..3d8d88834e2a278288f515fd64fdf1e1fa02ad12 100644
--- a/app/views/projects/blob/_blob.html.haml
+++ b/app/views/projects/blob/_blob.html.haml
@@ -1,4 +1,4 @@
-.gray-content-block.top-block
+.nav-block
   .tree-ref-holder
     = render 'shared/ref_switcher', destination: 'blob', path: @path
 
diff --git a/app/views/projects/blob/diff.html.haml b/app/views/projects/blob/diff.html.haml
index f3b01ff3288b1723eecaa8f266eae7111746514c..2e913802be199c3612507158fab133d12c36f8e3 100644
--- a/app/views/projects/blob/diff.html.haml
+++ b/app/views/projects/blob/diff.html.haml
@@ -11,7 +11,7 @@
       %td.old_line.diff-line-num{data: {linenumber: line_old}}
         = link_to raw(line_old), "#"
       %td.new_line= link_to raw(line_new) , "#"
-      %td.line_content.noteable_line= ' ' * @form.indent + line
+      %td.line_content.noteable_line==#{' ' * @form.indent}#{line}
 
   - if @form.unfold? && @form.bottom? && @form.to < @blob.loc
     %tr.line_holder{ id: @form.to }
diff --git a/app/views/projects/blob/edit.html.haml b/app/views/projects/blob/edit.html.haml
index 09fa148b129b03ec047f78748462478afe4784df..a279e6eda558c83c7f0fdf545de28cd4bdd28b50 100644
--- a/app/views/projects/blob/edit.html.haml
+++ b/app/views/projects/blob/edit.html.haml
@@ -2,7 +2,7 @@
 = render "header_title"
 
 .file-editor
-  %ul.center-top-menu.no-bottom.js-edit-mode
+  %ul.nav-links.no-bottom.js-edit-mode
     %li.active
       = link_to '#editor' do
         = icon('edit')
diff --git a/app/views/projects/blob/preview.html.haml b/app/views/projects/blob/preview.html.haml
index e7c3460ad7852ec49bfe84f0e0af009ec2634bd3..fed483d67881635a72510e4bc82c5175a6ee06dd 100644
--- a/app/views/projects/blob/preview.html.haml
+++ b/app/views/projects/blob/preview.html.haml
@@ -20,6 +20,6 @@
                 - else
                   %td.old_line
                   %td.new_line
-                  %td.line_content{class: "#{line.type}"}= raw diff_line_content(line.text)
+                  %td.line_content{class: "#{line.type}"}= diff_line_content(line.text)
         - else
           .nothing-here-block No changes.
diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml
index a234536723ee59f41803db5d3e060ebd325e813d..76a823d382873484d2aec0e4b0783e89d995e4ee 100644
--- a/app/views/projects/branches/_branch.html.haml
+++ b/app/views/projects/branches/_branch.html.haml
@@ -1,12 +1,12 @@
 - commit = @repository.commit(branch.target)
 - bar_graph_width_factor = @max_commits > 0 ? 100.0/@max_commits : 0
-- diverging_commit_counts = @repository.diverging_commit_counts(branch) 
+- diverging_commit_counts = @repository.diverging_commit_counts(branch)
 - number_commits_behind = diverging_commit_counts[:behind]
 - number_commits_ahead = diverging_commit_counts[:ahead]
 %li(class="js-branch-#{branch.name}")
   %div
     = link_to namespace_project_tree_path(@project.namespace, @project, branch.name) do
-      %strong.str-truncated= branch.name
+      %span.item-title.str-truncated= branch.name
     &nbsp;
     - if branch.name == @repository.root_ref
       %span.label.label-primary default
diff --git a/app/views/projects/builds/index.html.haml b/app/views/projects/builds/index.html.haml
index 3bbfdb1e3b00cce8f82a78f6f86d723475d7313d..bbb6944a65adedadcfe756dbeb9bed8ad504f567 100644
--- a/app/views/projects/builds/index.html.haml
+++ b/app/views/projects/builds/index.html.haml
@@ -6,9 +6,14 @@
     - if can?(current_user, :manage_builds, @project)
       .pull-left.hidden-xs
         - if @all_builds.running_or_pending.any?
-          = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project), data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
+          = link_to 'Cancel running', cancel_all_namespace_project_builds_path(@project.namespace, @project),
+            data: { confirm: 'Are you sure?' }, class: 'btn btn-danger', method: :post
 
-  %ul.center-top-menu
+        = link_to ci_lint_path, class: 'btn btn-default' do
+          = icon('wrench')
+          %span CI Lint
+
+  %ul.nav-links
     %li{class: ('active' if @scope.nil?)}
       = link_to project_builds_path(@project) do
         All
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 5b7ecce86ab2b9041019e526650a50e45eb124a0..2be572d3b10edfe9f690066c7d6add3fca753f0a 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -14,7 +14,7 @@
 
   #up-build-trace
   - if @commit.matrix_for_ref?(@build.ref)
-    %ul.center-top-menu.no-top.no-bottom
+    %ul.nav-links.no-top.no-bottom
       - @commit.latest_builds_for_ref(@build.ref).each do |build|
         %li{class: ('active' if build == @build) }
           = link_to namespace_project_build_path(@project.namespace, @project, build) do
@@ -89,9 +89,15 @@
             Test coverage
           %h1 #{@build.coverage}%
 
-      - if current_user && can?(current_user, :download_build_artifacts, @project) && @build.download_url
-        .build-widget.center
-          = link_to "Download artifacts", @build.download_url, class: 'btn btn-sm btn-primary'
+      - if current_user && can?(current_user, :read_build_artifacts, @project) && @build.artifacts?
+
+        .build-widget.artifacts
+          %h4.title Build artifacts
+          .center
+            .btn-group{ role: :group }
+              = link_to "Download", @build.artifacts_download_url, class: 'btn btn-sm btn-primary'
+              - if @build.artifacts_browser_supported?
+                = link_to "Browse", @build.artifacts_browse_url, class: 'btn btn-sm btn-primary'
 
       .build-widget
         %h4.title
diff --git a/app/views/projects/buttons/_dropdown.html.haml b/app/views/projects/buttons/_dropdown.html.haml
index f9ab78e7874a744e4f86bcfe7ba1e86bc7371a69..511863d774e82bcd754ff21c95fccf43d813c8a3 100644
--- a/app/views/projects/buttons/_dropdown.html.haml
+++ b/app/views/projects/buttons/_dropdown.html.haml
@@ -1,6 +1,6 @@
 - if current_user
-  %span.dropdown
-    %a.dropdown-new.btn.btn-new{href: '#', "data-toggle" => "dropdown"}
+  .btn-group
+    %a.btn.dropdown-toggle{href: '#', "data-toggle" => "dropdown"}
       = icon('plus')
     %ul.dropdown-menu.dropdown-menu-right.project-home-dropdown
       - if can?(current_user, :create_issue, @project)
diff --git a/app/views/projects/commit/_ci_menu.html.haml b/app/views/projects/commit/_ci_menu.html.haml
index f74f8b427ecaac0401fd239b7638abac1f57f056..ea33aa472a65dfd78c8d959613f11dc6e09bfb29 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.no-top.no-bottom.commit-ci-menu
+%ul.nav-links.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 ddb77fd796b13f03c74cc7e9aea68e8e0bc46e37..bbe820b884250340da09cd306c41395d7c74df41 100644
--- a/app/views/projects/commit/_commit_box.html.haml
+++ b/app/views/projects/commit/_commit_box.html.haml
@@ -50,7 +50,7 @@
 .commit-info-row.branches
   %i.fa.fa-spinner.fa-spin
 
-.commit-box.gray-content-block.middle-block
+.commit-box.content-block
   %h3.commit-title
     = markdown escape_once(@commit.title), pipeline: :single_line
   - if @commit.description.present?
diff --git a/app/views/projects/commit/builds.html.haml b/app/views/projects/commit/builds.html.haml
index 99d62503a949ce7e8634532f9b58a3fa9d1a67b0..7118a4846c6744fa4ecdb69d1e6bca3c6e4e6e2b 100644
--- a/app/views/projects/commit/builds.html.haml
+++ b/app/views/projects/commit/builds.html.haml
@@ -1,6 +1,7 @@
 - page_title "Builds", "#{@commit.title} (#{@commit.short_id})", "Commits"
 = render "projects/commits/header_title"
-= render "commit_box"
+.prepend-top-default
+  = render "commit_box"
 = render "ci_menu"
 
 = render "builds"
diff --git a/app/views/projects/commit/show.html.haml b/app/views/projects/commit/show.html.haml
index 58aa45e8d2c7dab4822939179ab96c6b2b0b1f94..05dbe5ebea41292ccdad88a99c81ce54931b7b47 100644
--- a/app/views/projects/commit/show.html.haml
+++ b/app/views/projects/commit/show.html.haml
@@ -2,10 +2,13 @@
 - page_description  @commit.description
 
 = render "projects/commits/header_title"
-= render "commit_box"
+
+.prepend-top-default
+  = render "commit_box"
 - if @ci_commit
   = render "ci_menu"
 - else
   %div.block-connector
-= render "projects/diffs/diffs", diffs: @diffs, project: @project
+= render "projects/diffs/diffs", diffs: @diffs, project: @project,
+         diff_refs: @diff_refs
 = render "projects/notes/notes_with_form"
diff --git a/app/views/projects/commit_statuses/_commit_status.html.haml b/app/views/projects/commit_statuses/_commit_status.html.haml
index 74a05df24d3db44b5365de2cc6f562325156cd12..1736dccaf3c755c3614345159c95381e4f454614 100644
--- a/app/views/projects/commit_statuses/_commit_status.html.haml
+++ b/app/views/projects/commit_statuses/_commit_status.html.haml
@@ -66,8 +66,8 @@
 
   %td
     .pull-right
-      - if current_user && can?(current_user, :download_build_artifacts, commit_status.project) && commit_status.download_url
-        = link_to commit_status.download_url, title: 'Download artifacts' do
+      - if current_user && can?(current_user, :read_build_artifacts, commit_status.project) && commit_status.artifacts?
+        = link_to commit_status.artifacts_download_url, title: 'Download artifacts' do
           %i.fa.fa-download
       - if current_user && can?(current_user, :manage_builds, commit_status.project)
         - if commit_status.active?
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index 012825f0fdb5973c3a79140d34532a1994c49690..7f2903589a90dd501053623795d26d2625676958 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -11,7 +11,7 @@
 = cache(cache_key) do
   %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
     .commit-row-title
-      %strong.str-truncated
+      %span.item-title.str-truncated
         = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
         - if commit.description?
           %a.text-expander.js-toggle-button ...
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index fcccb002d7e8d482f5358ae34bfc459304fae546..498c5e05b321edeb3e6e51d6a3d04a492dceb76a 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu
+%ul.nav-links
   = nav_link(controller: [:commit, :commits]) do
     = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
       Commits
diff --git a/app/views/projects/commits/show.html.haml b/app/views/projects/commits/show.html.haml
index 034057da42eb5299618a05e74a63147d9f1287b6..ede64d47ab339862072a141a6e66d75b16d70183 100644
--- a/app/views/projects/commits/show.html.haml
+++ b/app/views/projects/commits/show.html.haml
@@ -6,7 +6,7 @@
 
 = render "head"
 
-.gray-content-block
+.gray-content-block.second-block
   .tree-ref-holder
     = render 'shared/ref_switcher', destination: 'commits'
 
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index 51088a7dea89b0e97622dde7a1b0ba641ea8c0d8..da731f28bb608109b7b221601e9069a0c4606d39 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -9,7 +9,7 @@
 - if @commits.present?
   .prepend-top-default
     = render "projects/commits/commit_list"
-    = render "projects/diffs/diffs", diffs: @diffs, project: @project
+    = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
 - else
   .light-well.prepend-top-default
     .center
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index f9d661d59d291b83a15d7ed2a43cfb1f0bce868d..d668f483bcb3e40fec09247d8c07c4896949e275 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -1,9 +1,9 @@
 - if diff_view == 'parallel'
   - fluid_layout true
 
-- diff_files = safe_diff_files(diffs)
+- diff_files = safe_diff_files(diffs, diff_refs)
 
-.gray-content-block.middle-block.oneline-block
+.content-block.oneline-block
   .inline-parallel-buttons
     .btn-group
       = inline_diff_btn
diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml
index 37fd1b1ec8a34d4ce6a276f4e38e681af01164a0..a15d147ab051109319649aea5c726d8d369747a7 100644
--- a/app/views/projects/diffs/_parallel_view.html.haml
+++ b/app/views/projects/diffs/_parallel_view.html.haml
@@ -1,42 +1,40 @@
 / Side-by-side diff view
-%div.text-file.diff-wrap-lines
+%div.text-file.diff-wrap-lines.code.file-content.js-syntax-highlight
   %table
-    - parallel_diff(diff_file, index).each do |line|
-      - type_left = line[0]
-      - line_number_left = line[1]
-      - line_content_left = line[2]
-      - line_code_left = line[3]
-      - type_right = line[4]
-      - line_number_right = line[5]
-      - line_content_right = line[6]
-      - line_code_right = line[7]
-
+    - diff_file.parallel_diff_lines.each do |line|
+      - left = line[:left]
+      - right = line[:right]
       %tr.line_holder.parallel
-        - if type_left == 'match'
-          = render "projects/diffs/match_line_parallel", { line: line_content_left,
-          line_old: line_number_left, line_new: line_number_right }
-        - elsif type_left ==  'old' || type_left.nil?
-          %td.old_line{id: line_code_left, class: "#{type_left}"}
-            = link_to raw(line_number_left), "##{line_code_left}", id: line_code_left
+        - if left[:type] == 'match'
+          = render "projects/diffs/match_line_parallel", { line: left[:text],
+          line_old: left[:number], line_new: right[:number] }
+        - elsif left[:type] == 'nonewline'
+          %td.old_line
+            %td.line_content.parallel.matched= left[:text]
+          %td.new_line
+            %td.line_content.parallel.matched= left[:text]
+        - else
+          %td.old_line{id: left[:line_code], class: "#{left[:type]}"}
+            = link_to raw(left[:number]), "##{left[:line_code]}", id: left[:line_code]
             - if @comments_allowed && can?(current_user, :create_note, @project)
-              = link_to_new_diff_note(line_code_left, 'old')
-            %td.line_content{class: "parallel noteable_line #{type_left} #{line_code_left}", "line_code" => line_code_left }= raw line_content_left
+              = link_to_new_diff_note(left[:line_code], 'old')
+            %td.line_content{class: "parallel noteable_line #{left[:type]} #{left[:line_code]}", data: { line_code: left[:line_code] }}= diff_line_content(left[:text])
 
-          - if type_right == 'new'
+          - if right[:type] == 'new'
             - new_line_class = 'new'
-            - new_line_code = line_code_right
+            - new_line_code = right[:line_code]
           - else
             - new_line_class = nil
-            - new_line_code = line_code_left
+            - new_line_code = left[:line_code]
 
-          %td.new_line{id: new_line_code, class: "#{new_line_class}", data: { linenumber: line_number_right }}
-            = link_to raw(line_number_right), "##{new_line_code}", id: new_line_code
+          %td.new_line{id: new_line_code, class: "#{new_line_class}", data: { linenumber: right[:number] }}
+            = link_to raw(right[:number]), "##{new_line_code}", id: new_line_code
             - if @comments_allowed && can?(current_user, :create_note, @project)
-              = link_to_new_diff_note(line_code_right, 'new')
-            %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", "line_code" => new_line_code}= raw line_content_right
+              = link_to_new_diff_note(right[:line_code], 'new')
+            %td.line_content.parallel{class: "noteable_line #{new_line_class} #{new_line_code}", data: { line_code: new_line_code }}= diff_line_content(right[:text])
 
       - if @reply_allowed
-        - comments_left, comments_right = organize_comments(type_left, type_right, line_code_left, line_code_right)
+        - comments_left, comments_right = organize_comments(left[:type], right[:type], left[:line_code], right[:line_code])
         - if comments_left.present? || comments_right.present?
           = render "projects/notes/diff_notes_with_reply_parallel", notes_left: comments_left, notes_right: comments_right
 
diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml
index 977ca423f75323cd1b13ac18c5307a7e64cca777..f4fc6caba0f31ed557b6f4dffa0ef427eeeeb748 100644
--- a/app/views/projects/diffs/_text_file.html.haml
+++ b/app/views/projects/diffs/_text_file.html.haml
@@ -3,9 +3,10 @@
   .suppressed-container
     %a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show.
 
-%table.text-file{class: "#{'hide' if too_big}"}
+%table.text-file.code.js-syntax-highlight{ class: too_big ? 'hide' : '' }
+
   - last_line = 0
-  - diff_file.diff_lines.each_with_index do |line, index|
+  - diff_file.highlighted_diff_lines.each_with_index do |line, index|
     - type = line.type
     - last_line = line.new_pos
     - line_code = generate_line_code(diff_file.file_path, line)
@@ -14,14 +15,18 @@
       - if type == "match"
         = render "projects/diffs/match_line", {line: line.text,
           line_old: line_old, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file}
+      - elsif type == 'nonewline'
+        %td.old_line.diff-line-num
+        %td.new_line.diff-line-num
+        %td.line_content.matched= line.text
       - else
         %td.old_line
           = link_to raw(type == "new" ? "&nbsp;" : line_old), "##{line_code}", id: line_code
           - if @comments_allowed && can?(current_user, :create_note, @project)
             = link_to_new_diff_note(line_code)
         %td.new_line{data: {linenumber: line.new_pos}}
-          = link_to raw(type == "old" ? "&nbsp;" : line.new_pos) , "##{line_code}", id: line_code
-        %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
+          = link_to raw(type == "old" ? "&nbsp;" : line.new_pos), "##{line_code}", id: line_code
+        %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text)
 
     - if @reply_allowed
       - comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at)
diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml
index 31e752c664951768e28f149177a0ff3047db086e..8a99aceef7f87f5214d855e18fba6f7774c5f760 100644
--- a/app/views/projects/edit.html.haml
+++ b/app/views/projects/edit.html.haml
@@ -1,6 +1,6 @@
 - @blank_container = true
 
-.project-edit-container
+.project-edit-container.prepend-top-default
   .project-edit-errors
   .project-edit-content
     .panel.panel-default
diff --git a/app/views/projects/empty.html.haml b/app/views/projects/empty.html.haml
index 503d156661eac9df3bfef0d021f1d2629072c2d5..b34d106d56513e63d084078f063e216b504c4c86 100644
--- a/app/views/projects/empty.html.haml
+++ b/app/views/projects/empty.html.haml
@@ -1,3 +1,5 @@
+- @no_container = true
+
 = content_for :flash_message do
   - if current_user && can?(current_user, :download_code, @project)
     = render 'shared/no_ssh'
@@ -17,40 +19,41 @@
       file to this project.
 
 - if can?(current_user, :download_code, @project)
-  .prepend-top-20
-  .empty_wrapper
-    %h3.page-title-empty
-      Command line instructions
-    %div.git-empty
-      %fieldset
-        %h5 Git global setup
-        %pre.light-well
-          :preserve
-            git config --global user.name "#{h git_user_name}"
-            git config --global user.email "#{h git_user_email}"
+  %div{ class: container_class }
+    .prepend-top-20
+    .empty_wrapper
+      %h3.page-title-empty
+        Command line instructions
+      %div.git-empty
+        %fieldset
+          %h5 Git global setup
+          %pre.light-well
+            :preserve
+              git config --global user.name "#{h git_user_name}"
+              git config --global user.email "#{h git_user_email}"
 
-      %fieldset
-        %h5 Create a new repository
-        %pre.light-well
-          :preserve
-            git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
-            cd #{h @project.path}
-            touch README.md
-            git add README.md
-            git commit -m "add README"
-            git push -u origin master
+        %fieldset
+          %h5 Create a new repository
+          %pre.light-well
+            :preserve
+              git clone #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+              cd #{h @project.path}
+              touch README.md
+              git add README.md
+              git commit -m "add README"
+              git push -u origin master
 
-      %fieldset
-        %h5 Existing folder or Git repository
-        %pre.light-well
-          :preserve
-            cd existing_folder
-            git init
-            git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
-            git add .
-            git commit
-            git push -u origin master
+        %fieldset
+          %h5 Existing folder or Git repository
+          %pre.light-well
+            :preserve
+              cd existing_folder
+              git init
+              git remote add origin #{ content_tag(:span, default_url_to_repo, class: 'clone')}
+              git add .
+              git commit
+              git push -u origin master
 
-        - if can? current_user, :remove_project, @project
-          .prepend-top-20
-            = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
+          - if can? current_user, :remove_project, @project
+            .prepend-top-20
+              = link_to 'Remove project', [@project.namespace.becomes(Namespace), @project], data: { confirm: remove_project_message(@project)}, method: :delete, class: "btn btn-remove pull-right"
diff --git a/app/views/projects/find_file/show.html.haml b/app/views/projects/find_file/show.html.haml
index 40a2a61d8a1557ac1a8d41b173bbb23512636862..905f6bbbd48f7684f447cbcc402c22250fadb1aa 100644
--- a/app/views/projects/find_file/show.html.haml
+++ b/app/views/projects/find_file/show.html.haml
@@ -10,7 +10,7 @@
         = link_to namespace_project_tree_path(@project.namespace, @project, @ref) do
           = @project.path
       %li.file-finder
-        %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path'}
+        %input#file_find.form-control.file-finder-input{type: "text", placeholder: 'Find by path', autocomplete: 'off'}
 
   %div.tree-content-holder
     .table-holder
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index a47643bd09c642add4a0abca355b2253b0b9881f..79a56647c536ba79a4836a05eeee991d972dea12 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,4 +1,4 @@
-%ul.center-top-menu
+%ul.nav-links
   = nav_link(action: :show) do
     = link_to 'Contributors', namespace_project_graph_path
   = nav_link(action: :commits) do
diff --git a/app/views/projects/issues/_closed_by_box.html.haml b/app/views/projects/issues/_closed_by_box.html.haml
index de415ae51a41d1979f408fc8102716dfd2c98f06..38469ed4774c950c0cb4e88c18c377ef806bb6e4 100644
--- a/app/views/projects/issues/_closed_by_box.html.haml
+++ b/app/views/projects/issues/_closed_by_box.html.haml
@@ -1,2 +1,4 @@
-.issue-closed-by-widget.gray-content-block.second-block.white
-  This issue will be closed automatically when merge request #{markdown(merge_requests_sentence(@closed_by_merge_requests), pipeline: :gfm)} is accepted.
+.issue-closed-by-widget.second-block
+  - pluralized_mr_this = merge_request_count > 1 ? "these" : "this"
+  - pluralized_mr_is = merge_request_count > 1 ? "are" : "is"
+  When #{pluralized_mr_this} merge #{"request".pluralize(merge_request_count)} #{pluralized_mr_is} accepted, this issue will be closed automatically.
diff --git a/app/views/projects/issues/_discussion.html.haml b/app/views/projects/issues/_discussion.html.haml
index dc434cf38c4ab006432903456cfd5dace77a4865..673020a4e3010cddf63cdb0d3ec415baf8e2180a 100644
--- a/app/views/projects/issues/_discussion.html.haml
+++ b/app/views/projects/issues/_discussion.html.haml
@@ -1,9 +1,7 @@
 - content_for :note_actions do
   - if can?(current_user, :update_issue, @issue)
-    - if @issue.closed?
-      = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-reopen js-note-target-reopen', title: 'Reopen Issue'
-    - else
-      = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true), method: :put, class: 'btn btn-nr btn-grouped btn-close js-note-target-close', title: 'Close Issue'
+    = link_to 'Reopen Issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen btn-comment js-note-target-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen Issue'
+    = link_to 'Close Issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close btn-comment js-note-target-close #{issue_button_visibility(@issue, true)}", title: 'Close Issue'
 
 #notes
   = render 'projects/notes/notes_with_form'
diff --git a/app/views/projects/issues/_issues.html.haml b/app/views/projects/issues/_issues.html.haml
index e0e89b764d59eaf9d93adaf5c8a08625049636c5..f34f3c0573743d4cb746e3e51399c6d1054907bb 100644
--- a/app/views/projects/issues/_issues.html.haml
+++ b/app/views/projects/issues/_issues.html.haml
@@ -5,9 +5,4 @@
       .nothing-here-block No issues to show
 
 - if @issues.present?
-  .issuable-filter-count
-    %span.pull-right
-      = number_with_delimiter(@issues.total_count)
-      issues for this filter
-
   = paginate @issues, theme: "gitlab"
diff --git a/app/views/projects/issues/_merge_requests.html.haml b/app/views/projects/issues/_merge_requests.html.haml
index 254968e4f678f7850a25b3c74d33a77dd8b90531..640a1962ffc6e941d0d91fe46242a4a7b646ae74 100644
--- a/app/views/projects/issues/_merge_requests.html.haml
+++ b/app/views/projects/issues/_merge_requests.html.haml
@@ -1,7 +1,7 @@
 -if @merge_requests.any?
   %h2.merge-requests-title
     = pluralize(@merge_requests.count, 'Related Merge Request')
-  %ul.bordered-list
+  %ul.unstyled-list
     - has_any_ci = @merge_requests.any?(&:ci_commit)
     - @merge_requests.each do |merge_request|
       %li
@@ -11,7 +11,7 @@
           - elsif has_any_ci
             = icon('blank fw')
         %span.merge-request-id
-          \##{merge_request.iid}
+          \!#{merge_request.iid}
         %span.merge-request-info
           %strong
             = link_to_gfm merge_request.title, merge_request_path(merge_request), class: "row_title"
@@ -24,3 +24,5 @@
             MERGED
           - elsif merge_request.closed?
             CLOSED
+    - if @closed_by_merge_requests.present?
+      = render partial: 'projects/issues/closed_by_box', locals: {merge_request_count: @merge_requests.count}
diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml
index f931a0d3b92bcdfc6f2bfb8830f6f23ebfe43ada..7ed898ce72fbc6f1997baa09b4a91a70a8de0225 100644
--- a/app/views/projects/issues/show.html.haml
+++ b/app/views/projects/issues/show.html.haml
@@ -53,9 +53,6 @@
     .gray-content-block.second-block.oneline-block
       = render 'votes/votes_block', votable: @issue
 
-    - if @closed_by_merge_requests.present?
-      = render 'projects/issues/closed_by_box'
-
     .row
       %section.col-md-9
         .issuable-discussion
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index bff3c3b283ddf33a3fee9104980120eb62136021..1c7de94acfdf426840494390098c1a5c12de96c6 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -1,8 +1,8 @@
 - content_for :note_actions do
   - if can?(current_user, :update_merge_request, @merge_request)
     - if @merge_request.open?
-      = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
+      = link_to 'Close', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request"
     - if @merge_request.closed?
-      = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
+      = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request"
 
 #notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/merge_requests/_merge_requests.html.haml b/app/views/projects/merge_requests/_merge_requests.html.haml
index 29d09d0a6521de10e026924c0ebbf8d0fee40507..5473fa191662af30e0ecc02be3652e041e453e4f 100644
--- a/app/views/projects/merge_requests/_merge_requests.html.haml
+++ b/app/views/projects/merge_requests/_merge_requests.html.haml
@@ -5,10 +5,5 @@
       .nothing-here-block No merge requests to show
 
 - if @merge_requests.present?
-  .issuable-filter-count
-    %span.pull-right
-      = number_with_delimiter(@merge_requests.total_count)
-      merge requests for this filter
-
   = paginate @merge_requests, theme: "gitlab"
 
diff --git a/app/views/projects/merge_requests/_new_submit.html.haml b/app/views/projects/merge_requests/_new_submit.html.haml
index a14943b15d3f1532ac6b0e94fb3162817988a16c..4c5a9818e3e636ee976600f88561b8f200916766 100644
--- a/app/views/projects/merge_requests/_new_submit.html.haml
+++ b/app/views/projects/merge_requests/_new_submit.html.haml
@@ -18,7 +18,7 @@
   = f.hidden_field :target_branch
 
 .mr-compare.merge-request
-  %ul.merge-request-tabs.center-top-menu.no-top.no-bottom
+  %ul.merge-request-tabs.nav-links.no-top.no-bottom
     %li.commits-tab
       = link_to url_for(params), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
         Commits
@@ -38,7 +38,7 @@
       = render "projects/merge_requests/show/commits"
     #diffs.diffs.tab-pane.active
       - if @diffs.present?
-        = render "projects/diffs/diffs", diffs: @diffs, project: @project
+        = render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @merge_request.diff_refs
       - elsif @commits.size > MergeRequestDiff::COMMITS_SAFE_SIZE
         .alert.alert-danger
           %h4 This comparison includes more than #{MergeRequestDiff::COMMITS_SAFE_SIZE} commits.
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 095876450a09e73e1a6e56a326959dfdcd1ec864..200bfa5ac4fb48b6fe96c5cb19a2065d3a3e2b24 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -45,7 +45,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.center-top-menu.no-top.no-bottom
+      %ul.merge-request-tabs.nav-links.no-top.no-bottom
         %li.notes-tab
           = link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do
             Discussion
diff --git a/app/views/projects/merge_requests/show/_diffs.html.haml b/app/views/projects/merge_requests/show/_diffs.html.haml
index d9cfc3d7ae943ba9df5f8afa7140a76e1463ec17..64cd484193e810d39f4774d45d18926f1eb341ae 100644
--- a/app/views/projects/merge_requests/show/_diffs.html.haml
+++ b/app/views/projects/merge_requests/show/_diffs.html.haml
@@ -1,5 +1,6 @@
 - if @merge_request_diff.collected?
-  = render "projects/diffs/diffs", diffs: params[:w] == '1' ? @merge_request.diffs_no_whitespace : @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, diff_refs: @merge_request.diff_refs
 - 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/_milestone.html.haml b/app/views/projects/milestones/_milestone.html.haml
index d6a44c9f0a1ce776e1267190b7c80da39976bf05..67d95ab0364d349cec2441263ae7f81a8ad7800a 100644
--- a/app/views/projects/milestones/_milestone.html.haml
+++ b/app/views/projects/milestones/_milestone.html.haml
@@ -21,10 +21,11 @@
       = render 'shared/milestone_expired', milestone: milestone
     .col-sm-6
       - if can?(current_user, :admin_milestone, milestone.project) and milestone.active?
-        = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs edit-milestone-link btn-grouped" do
-          %i.fa.fa-pencil-square-o
+        = link_to edit_namespace_project_milestone_path(milestone.project.namespace, milestone.project, milestone), class: "btn btn-xs" do
+          = icon('pencil-square-o')
           Edit
+        \
         = 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
+          = icon('trash-o')
           Delete
diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml
index 1670ea8741a3a07065f93f38a89e4dc13b771812..1142c58459292ed61921357c802f2428fad2199e 100644
--- a/app/views/projects/milestones/show.html.haml
+++ b/app/views/projects/milestones/show.html.haml
@@ -20,16 +20,16 @@
   .pull-right
     - if can?(current_user, :admin_milestone, @project)
       - 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"
+        = link_to 'Close Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :close }), method: :put, class: "btn btn-close btn-nr 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 'Reopen Milestone', namespace_project_milestone_path(@project.namespace, @project, @milestone, milestone: {state_event: :activate }), method: :put, class: "btn btn-reopen btn-nr 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
+      = link_to namespace_project_milestone_path(@project.namespace, @project, @milestone), data: { confirm: 'Are you sure?' }, method: :delete, class: "btn btn-grouped btn-nr btn-remove" do
+        = icon('trash-o')
         Delete
 
-      = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped" do
-        %i.fa.fa-pencil-square-o
+      = link_to edit_namespace_project_milestone_path(@project.namespace, @project, @milestone), class: "btn btn-grouped btn-nr" do
+        = icon('pencil-square-o')
         Edit
 
 .detail-page-description.gray-content-block.second-block
@@ -57,7 +57,7 @@
     %span.pull-right= @milestone.expires_at
   = milestone_progress_bar(@milestone)
 
-%ul.center-top-menu.no-top.no-bottom
+%ul.nav-links.no-top.no-bottom
   %li.active
     = link_to '#tab-issues', 'data-toggle' => 'tab' do
       Issues
diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml
index 3ccda1b381caf9a27210399683ece84899fe49d8..5d78652befa6b1a5bf84cad2509a3c132beb900a 100644
--- a/app/views/projects/notes/_edit_form.html.haml
+++ b/app/views/projects/notes/_edit_form.html.haml
@@ -6,5 +6,5 @@
       = render 'projects/notes/hints'
 
     .note-form-actions
-      = f.submit 'Save Comment', class: 'btn btn-primary btn-save btn-grouped js-comment-button'
-      = link_to  'Cancel', '#', class: 'btn btn-cancel note-edit-cancel'
+      = f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button'
+      = link_to  'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel'
diff --git a/app/views/projects/notes/_form.html.haml b/app/views/projects/notes/_form.html.haml
index acb6dc52a8e204f7fecfc7d2f70eeabe844041dc..f10a4145d623b2e5d5b2e8d6cba0d3605a2258ba 100644
--- a/app/views/projects/notes/_form.html.haml
+++ b/app/views/projects/notes/_form.html.haml
@@ -15,4 +15,4 @@
   .note-form-actions.clearfix
     = f.submit 'Add Comment', class: "btn btn-nr btn-create comment-btn btn-grouped js-comment-button"
     = yield(:note_actions)
-    %a.btn.btn-cancel.js-close-discussion-note-form Cancel
+    %a.btn.btn-nr.btn-cancel.js-close-discussion-note-form Cancel
diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml
index ca60dd239b234033e6106d5466ce86689ba83d52..62db86fb18171cd335b585f030e46ac361361c7e 100644
--- a/app/views/projects/notes/_notes.html.haml
+++ b/app/views/projects/notes/_notes.html.haml
@@ -2,10 +2,14 @@
   - @discussions.each do |discussion_notes|
     - note = discussion_notes.first
     - if note_for_main_target?(note)
+      - next if note.cross_reference_not_visible_for?(current_user)
+
       = render discussion_notes
     - else
       = render 'projects/notes/discussion', discussion_notes: discussion_notes
 - else
   - @notes.each do |note|
     - next unless note.author
+    - next if note.cross_reference_not_visible_for?(current_user)
+
     = render note
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index eb378b4260367a15996999c525430642ae2660fa..910eb6cf66ec871baa89b080abc4116eedf6a899 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -5,6 +5,16 @@
 .js-main-target-form
 - if can? current_user, :create_note, @project
   = render "projects/notes/form", view: diff_view
+- else
+  .disabled-comment-area
+    .disabled-profile
+    .disabled-comment
+      %span
+        Please
+        = link_to "register",new_user_session_path
+        or
+        = link_to "login",new_user_session_path
+        to post a comment
 
 :javascript
   var notes = 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/notes/discussions/_diff.html.haml b/app/views/projects/notes/discussions/_diff.html.haml
index 0301445b5b28f02c18eeb456acde6e2dba3bdc96..46962b184cee5331bf80c7f527ba7d565b162591 100644
--- a/app/views/projects/notes/discussions/_diff.html.haml
+++ b/app/views/projects/notes/discussions/_diff.html.haml
@@ -24,7 +24,7 @@
                 = raw(type == "new" ? "&nbsp;" : line.old_pos)
               %td.new_line
                 = raw(type == "old" ? "&nbsp;" : line.new_pos)
-              %td.line_content{class: "noteable_line #{type} #{line_code}", "line_code" => line_code}= raw diff_line_content(line.text)
+              %td.line_content{class: "noteable_line #{type} #{line_code}", line_code: line_code}= diff_line_content(line.text)
 
               - if line_code == note.line_code
                 = render "projects/notes/diff_notes_with_reply", notes: discussion_notes
diff --git a/app/views/projects/project_members/_new_project_member.html.haml b/app/views/projects/project_members/_new_project_member.html.haml
index d708b01a114873fda0195d43cc4cf09d29a45018..f0f3bb3c177eab2d04f5b1f01ef0135f4b6c4fb2 100644
--- a/app/views/projects/project_members/_new_project_member.html.haml
+++ b/app/views/projects/project_members/_new_project_member.html.haml
@@ -4,7 +4,7 @@
     .col-sm-10
       = users_select_tag(:user_ids, multiple: true, class: 'input-large', scope: :all, email_user: true)
       .help-block
-        Search for existing users or invite new ones using their email address.
+        Search for users by name, username, or email, or invite new ones using their email address.
 
   .form-group
     = f.label :access_level, "Project Access", class: 'control-label'
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 29225a3636455d35c8727c51ab0ef86e0eb6660a..6239a148905d2c761f28fd1ba003fffe742ac9e6 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -2,7 +2,7 @@
 = render "header_title"
 - @blank_container = true
 
-.project-members-page
+.project-members-page.prepend-top-default
   - if can?(current_user, :admin_project_member, @project)
     .panel.panel-default
       .panel-heading
diff --git a/app/views/projects/runners/index.html.haml b/app/views/projects/runners/index.html.haml
index 315afe4a764506b1e66e553d6accbc17b26cb68d..2d5b9f43c24da3103ec4c02b3ca9cba2253376d0 100644
--- a/app/views/projects/runners/index.html.haml
+++ b/app/views/projects/runners/index.html.haml
@@ -1,5 +1,6 @@
 - page_title "Runners"
-.light
+
+.light.prepend-top-default
   %p
     A 'runner' is a process which runs a build.
     You can setup as many runners as you need.
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index 8436be433b198e7d4ae008891a7c89961c42dd49..4310f038fc94e5b083c3713253218325759d8ea7 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -1,3 +1,5 @@
+- @no_container = true
+
 = content_for :meta_tags do
   - if current_user
     = auto_discovery_link_tag(:atom, namespace_project_path(@project.namespace, @project, format: :atom, private_token: current_user.private_token), title: "#{@project.name} activity")
@@ -8,11 +10,10 @@
     = render 'shared/no_password'
 
 = render 'projects/last_push'
-
 = render "home_panel"
 
 .project-stats.gray-content-block.second-block
-  %ul.nav.nav-pills
+  %ul.nav
     %li
       = link_to namespace_project_commits_path(@project.namespace, @project, current_ref) do
         = pluralize(number_with_delimiter(@project.commit_count), 'commit')
@@ -57,15 +58,17 @@
           = link_to add_contribution_guide_path(@project) do
             Add Contribution guide
 
-- if @project.archived?
-  .text-warning.center.prepend-top-20
-    %p
-      = icon("exclamation-triangle fw")
-      Archived project! Repository is read-only
-
 - if @repository.commit
   .content-block.second-block.white
-    = render 'projects/last_commit', commit: @repository.commit, project: @project
+    %div{ class: container_class }
+      = render 'projects/last_commit', commit: @repository.commit, project: @project
+
+%div{ class: container_class }
+  - if @project.archived?
+    .text-warning.center.prepend-top-20
+      %p
+        = icon("exclamation-triangle fw")
+        Archived project! Repository is read-only
 
-%div{class: "project-show-#{default_project_view}"}
-  = render default_project_view
+  %div{class: "project-show-#{default_project_view}"}
+    = render default_project_view
diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml
index 28b706c5c7e85f95f928229d0ca5944b1005dffd..399782273d308f233168814ae873b4c79096b4d0 100644
--- a/app/views/projects/tags/_tag.html.haml
+++ b/app/views/projects/tags/_tag.html.haml
@@ -3,7 +3,7 @@
 %li
   %div
     = link_to namespace_project_tag_path(@project.namespace, @project, tag.name) do
-      %strong
+      %span.item-title
         = icon('tag')
         = tag.name
     - if tag.message.present?
diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml
index b594d4f1f27674e40232a9f8ac46914b8c8e81a3..8c7f93f93b683b7c2ab588849ebc6acb1b00463a 100644
--- a/app/views/projects/tags/show.html.haml
+++ b/app/views/projects/tags/show.html.haml
@@ -18,7 +18,7 @@
         = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", 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
+    %span.item-title= @tag.name
     - if @tag.message.present?
       %span.light
         &nbsp;
diff --git a/app/views/projects/tree/_tree_content.html.haml b/app/views/projects/tree/_tree_content.html.haml
index 1927883513afbf2a2e0b96e8ea1973a97da658fe..558e6146ae993f7c860061ce3223fa21f3dd9149 100644
--- a/app/views/projects/tree/_tree_content.html.haml
+++ b/app/views/projects/tree/_tree_content.html.haml
@@ -1,6 +1,6 @@
 %div.tree-content-holder
   .table-holder
-    %table.table#tree-slider{class: "table_#{@hex_path} tree-table table-striped" }
+    %table.table#tree-slider{class: "table_#{@hex_path} tree-table" }
       %thead
         %tr
           %th Name
diff --git a/app/views/projects/tree/show.html.haml b/app/views/projects/tree/show.html.haml
index c57570afa0908b351e71f9c8cfc36fe1a1109a95..91fb2a44594760458e0e520b8cc46df9e78b92d4 100644
--- a/app/views/projects/tree/show.html.haml
+++ b/app/views/projects/tree/show.html.haml
@@ -5,13 +5,13 @@
     = auto_discovery_link_tag(:atom, namespace_project_commits_url(@project.namespace, @project, @ref, format: :atom, private_token: current_user.private_token), title: "#{@project.name}:#{@ref} commits")
 = render 'projects/last_push'
 
-.pull-right
+.tree-controls
   = render 'projects/find_file_link'
   - if can? current_user, :download_code, @project
     = render 'projects/repositories/download_archive', ref: @ref, btn_class: 'hidden-xs hidden-sm btn-grouped', split_button: true
 
 #tree-holder.tree-holder.clearfix
-  .gray-content-block.top-block
+  .nav-block
     = render 'projects/tree/tree_header', tree: @tree
 
   = render 'projects/tree/tree_content', tree: @tree
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index e6e6ad5bc4b631473f8970d19b05b2cd70f8d85c..69ba301e231f51b3763009b0b2410aa569051859 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -7,7 +7,7 @@
 
       = render 'projects/wikis/new'
 
-  %ul.center-top-menu
+  %ul.nav-links
     = nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
       = link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
 
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index f0547e9c0575d7259e853b44949b440d58d84604..53b37b1104e0449f5a2fe73ae9d7d5531a50b2fe 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -5,12 +5,9 @@
         %a.close{href: "#", "data-dismiss" => "modal"} ×
         %h3.page-title New Wiki Page
       .modal-body
-        = label_tag :new_wiki_path do
-          %span Page slug
-        = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
-        %p.hidden.text-danger{data: { error: "slug" }}
-          The page slug is invalid. Please don't use characters other then: a-z 0-9 _ - and /
-        %p.hint
-          Please don't use spaces.
+        .form-group
+          = label_tag :new_wiki_path do
+            %span Page slug
+          = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project)
         .form-actions
           = link_to 'Create Page', '#', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index 11c8c4f0eba81ae1273a88880999d332c314719b..dd27ea2b11bc6aad8b1e910e39def9b47b954967 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -3,14 +3,12 @@
 
 = render 'nav'
 .gray-content-block
-  .row
-    .col-sm-6
-      %h3.page-title.oneline
-        Git access for
-        %strong= @project_wiki.path_with_namespace
+  %span.oneline
+    Git access for
+    %strong= @project_wiki.path_with_namespace
 
-    .col-sm-6
-      = render "shared/clone_panel", project: @project_wiki
+  .pull-right
+    = render "shared/clone_panel", project: @project_wiki
 
 .git-empty.prepend-top-default
   %fieldset
diff --git a/app/views/search/_category.html.haml b/app/views/search/_category.html.haml
index 481451edb23ce0904bbb70b6e15fd2612faae374..2c3fca439f3e18d98b92624e84798ce511d80e92 100644
--- a/app/views/search/_category.html.haml
+++ b/app/views/search/_category.html.haml
@@ -1,4 +1,4 @@
-%ul.nav.nav-tabs.search-filter
+%ul.nav-links.search-filter
   - if @project
     %li{class: ("active" if @scope == 'blobs')}
       = link_to search_filter_path(scope: 'blobs') do
diff --git a/app/views/search/_results.html.haml b/app/views/search/_results.html.haml
index 2a38c98dcfcc17dc205f6c49d26b4f69c8a05452..d0e64537621f4e92f0c29717f1f09d28c7342b5b 100644
--- a/app/views/search/_results.html.haml
+++ b/app/views/search/_results.html.haml
@@ -1,7 +1,7 @@
 - if @search_results.empty?
   = render partial: "search/results/empty"
 - else
-  %p.light
+  .gray-content-block
     Search results for
     %code
       = @search_term
diff --git a/app/views/search/show.html.haml b/app/views/search/show.html.haml
index f4f3dcfc29ff5ba1ebe68709264f50e7adaa4ac9..215dbb3909e697ef86cbbd00161707d968005511 100644
--- a/app/views/search/show.html.haml
+++ b/app/views/search/show.html.haml
@@ -1,5 +1,7 @@
 - page_title @search_term
-= render 'search/form'
+
+.prepend-top-default
+  = render 'search/form'
 - if @search_term
   = render 'search/category'
   = render 'search/results'
diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml
index 687a59c270f9f35d5b940aa740ac60b18f453839..faf7e49ed2902fb6c43a0f54cae83ea9fb558fe7 100644
--- a/app/views/shared/_clone_panel.html.haml
+++ b/app/views/shared/_clone_panel.html.haml
@@ -1,7 +1,7 @@
 - project = project || @project
 
-.git-clone-holder
-  .btn-group.clone-options
+.git-clone-holder.input-group
+  .input-group-btn
     %a#clone-dropdown.clone-dropdown-btn.btn{href: '#', 'data-toggle' => 'dropdown'}
       %span
         = default_clone_protocol.upcase
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 8495774accc13c27edb8df55d75df7c11a3d5fd0..c38d9313dba6e15990e95df594c9714a3b4d9187 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,4 +1,4 @@
-.btn-group.btn-group-next.event-filter
+%ul.nav-links.event-filter
   = event_filter_link EventFilter.push, 'Push events'
   = event_filter_link EventFilter.merged, 'Merge events'
   = event_filter_link EventFilter.comments, 'Comments'
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 2bc98983d672d2f7e91cdadfeb7b9325852e5871..1bef1de433afc161771a14714ed07fd6f3dcef5d 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -9,5 +9,4 @@
           %i.fa.fa-link
           = i
   .blob-content{data: {blob_id: blob.id}}
-    :preserve
-      #{highlight(blob.name, blob.data)}
+    = highlight(blob.name, blob.data)
diff --git a/app/views/shared/_milestones_filter.html.haml b/app/views/shared/_milestones_filter.html.haml
index cbdecda4ffff27610cbb102031a52f025f97758c..f77feeb79cd1f444381dc3e191ce0d57d1f806d4 100644
--- a/app/views/shared/_milestones_filter.html.haml
+++ b/app/views/shared/_milestones_filter.html.haml
@@ -1,5 +1,5 @@
 .milestones-filters
-  %ul.center-top-menu
+  %ul.nav-links
     %li{class: ("active" if params[:state].blank? || params[:state] == 'opened')}
       = link_to milestones_filter_path(state: 'opened') do
         Open
diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml
index a54c5fa8c33f848b016e6faa0d1cef504f88ae1f..778b20fb4f27bc2561a9e5d0d7b12a95712931da 100644
--- a/app/views/shared/groups/_group.html.haml
+++ b/app/views/shared/groups/_group.html.haml
@@ -11,7 +11,7 @@
 
   = image_tag group_icon(group), class: "avatar s46 hidden-xs"
   = link_to group, class: 'group-name' do
-    %strong= group.name
+    %span.item-title= group.name
 
   - if group_member
     as
@@ -19,4 +19,3 @@
 
   %div.light
     #{pluralize(group.projects.count, "project")}, #{pluralize(group.users.count, "user")}
-
diff --git a/app/views/shared/issuable/_filter.html.haml b/app/views/shared/issuable/_filter.html.haml
index be06738eac900b5b1b07c87c2c0438187c145d1c..8d6f47b38efbbd8852ae8b9434e0a85fa83c1dbb 100644
--- a/app/views/shared/issuable/_filter.html.haml
+++ b/app/views/shared/issuable/_filter.html.haml
@@ -1,25 +1,29 @@
 .issues-filters
   .issues-state-filters
-    %ul.center-top-menu
+    %ul.nav-links
+      - if defined?(type) && type == :merge_requests
+        - page_context_word = 'merge requests'
+      - else
+        - page_context_word = 'issues'
       %li{class: ("active" if params[:state] == 'opened')}
-        = link_to page_filter_path(state: 'opened') do
+        = link_to page_filter_path(state: 'opened'), title: "Filter by #{page_context_word} that are currently opened." do
           #{state_filters_text_for(:opened, @project)}
 
       - if defined?(type) && type == :merge_requests
         %li{class: ("active" if params[:state] == 'merged')}
-          = link_to page_filter_path(state: 'merged') do
+          = link_to page_filter_path(state: 'merged'), title: 'Filter by merge requests that are currently merged.' do
             #{state_filters_text_for(:merged, @project)}
 
         %li{class: ("active" if params[:state] == 'closed')}
-          = link_to page_filter_path(state: 'closed') do
+          = link_to page_filter_path(state: 'closed'), title: 'Filter by merge requests that are currently closed and unmerged.' do
             #{state_filters_text_for(:closed, @project)}
       - else
         %li{class: ("active" if params[:state] == 'closed')}
-          = link_to page_filter_path(state: 'closed') do
+          = link_to page_filter_path(state: 'closed'), title: 'Filter by issues that are currently closed.' do
             #{state_filters_text_for(:closed, @project)}
 
       %li{class: ("active" if params[:state] == 'all')}
-        = link_to page_filter_path(state: 'all') do
+        = link_to page_filter_path(state: 'all'), title: "Show all #{page_context_word}." do
           #{state_filters_text_for(:all, @project)}
 
   .issues-details-filters.gray-content-block
diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml
index 2299112bec7893fb4dbd4a47ea908ebe24a01f2a..3092ff542423bcf58d85b807b0d43912d7453aa8 100644
--- a/app/views/shared/issuable/_sidebar.html.haml
+++ b/app/views/shared/issuable/_sidebar.html.haml
@@ -10,6 +10,9 @@
       .value
         - if issuable.assignee
           %strong= link_to_member(@project, issuable.assignee, size: 24)
+          - if issuable.instance_of?(MergeRequest) && !issuable.can_be_merged_by?(issuable.assignee)
+            %a.pull-right.cannot-be-merged{href: '#', data: {toggle: 'tooltip'}, title: 'Not allowed to merge'}
+              = icon('exclamation-triangle')
         - else
           .light None
 
@@ -69,15 +72,16 @@
             You're not receiving notifications from this thread.
           .subscribed{class: ( 'hidden' unless subscribed )}
             You're receiving notifications because you're subscribed to this thread.
+
     - project_ref = cross_project_reference(@project, issuable)
     .block
       .title
       .cross-project-reference
-        %span#cross-project-reference
+        %span
           Reference:
-          %a{href: '#', title:project_ref}
+          %cite{title: project_ref}
             = project_ref
-        = clipboard_button(clipboard_target: 'span#cross-project-reference')
+        = clipboard_button(clipboard_text: project_ref)
 
   :javascript
     new Subscription("#{toggle_subscription_path(issuable)}");
diff --git a/app/views/sherlock/queries/show.html.haml b/app/views/sherlock/queries/show.html.haml
index 4a84348ac82582f753d40c81826bdb17684bb4b5..83f61ce4b073d2b2ca73c4befbea598688cc9042 100644
--- a/app/views/sherlock/queries/show.html.haml
+++ b/app/views/sherlock/queries/show.html.haml
@@ -1,7 +1,7 @@
 - page_title t('sherlock.title'), t('sherlock.transaction'), t('sherlock.query')
 - header_title t('sherlock.title'), sherlock_transactions_path
 
-%ul.center-top-menu
+%ul.nav-links
   %li.active
     %a(href="#tab-general" data-toggle="tab")
       = t('sherlock.general')
diff --git a/app/views/sherlock/transactions/_queries.html.haml b/app/views/sherlock/transactions/_queries.html.haml
index b7e0162e80db835d22bc1b3aa44933ee6c3141ad..b8d93e9ff45d4e63f9d82791a9432c89236be010 100644
--- a/app/views/sherlock/transactions/_queries.html.haml
+++ b/app/views/sherlock/transactions/_queries.html.haml
@@ -8,7 +8,7 @@
         %tr
           %th= t('sherlock.time')
           %th= t('sherlock.query')
-          %td
+          %th
       %tbody
         - @transaction.sorted_queries.each do |query|
           %tr
diff --git a/app/views/sherlock/transactions/show.html.haml b/app/views/sherlock/transactions/show.html.haml
index 3c8ffb066484125b3cb1b2880fb7e3f8e305530c..9d4b0b2724c2c3be77b7d7aa29ecb88fd0284df2 100644
--- a/app/views/sherlock/transactions/show.html.haml
+++ b/app/views/sherlock/transactions/show.html.haml
@@ -1,7 +1,7 @@
 - page_title t('sherlock.title'), t('sherlock.transaction')
 - header_title t('sherlock.title'), sherlock_transactions_path
 
-%ul.center-top-menu
+%ul.nav-links
   %li.active
     %a(href="#tab-general" data-toggle="tab")
       = t('sherlock.general')
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index ce17fc7bca16155ac2678c80dbbb2a73a6f0c87f..3bfd781e51d5a6d87fae66948d8860488764f4f8 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,6 +1,7 @@
 - page_title       @user.name
 - page_description @user.bio
 - header_title     @user.name, user_path(@user)
+- @no_container = true
 
 = content_for :meta_tags do
   = auto_discovery_link_tag(:atom, user_url(@user, format: :atom), title: "#{@user.name} activity")
@@ -8,6 +9,25 @@
 = render 'shared/show_aside'
 
 .cover-block
+  .cover-controls
+    - if @user == current_user
+      = link_to profile_path, class: 'btn btn-gray' do
+        = icon('pencil')
+    - elsif current_user
+      %span.report-abuse
+        - if @user.abuse_report
+          %button.btn.btn-danger{ title: 'Already reported for abuse',
+            data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
+            = icon('exclamation-circle')
+        - else
+          = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn btn-gray',
+            title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
+            = icon('exclamation-circle')
+    - if current_user
+      &nbsp;
+      = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
+        = icon('rss')
+
   .avatar-holder
     = link_to avatar_icon(@user, 400), target: '_blank' do
       = image_tag avatar_icon(@user, 90), class: "avatar s90", alt: ''
@@ -47,74 +67,56 @@
         = icon('map-marker')
         = @user.location
 
+  %ul.nav-links.center
+    %li.active
+      = link_to "#activity", 'data-toggle' => 'tab' do
+        Activity
+    - if @groups.any?
+      %li
+        = link_to "#groups", 'data-toggle' => 'tab' do
+          Groups
+    - if @contributed_projects.present?
+      %li
+        = link_to "#contributed", 'data-toggle' => 'tab' do
+          Contributed projects
+    - if @projects.present?
+      %li
+        = link_to "#personal", 'data-toggle' => 'tab' do
+          Personal projects
 
-  .cover-controls
-    - if @user == current_user
-      = link_to profile_path, class: 'btn btn-gray' do
-        = icon('pencil')
-    - elsif current_user
-      %span.report-abuse
-        - if @user.abuse_report
-          %button.btn.btn-danger{ title: 'Already reported for abuse',
-            data: { toggle: 'tooltip', placement: 'left', container: 'body' }}
-            = icon('exclamation-circle')
-        - else
-          = link_to new_abuse_report_path(user_id: @user.id), class: 'btn btn-gray',
-            title: 'Report abuse', data: {toggle: 'tooltip', placement: 'left', container: 'body'} do
-            = icon('exclamation-circle')
-    - if current_user
-      &nbsp;
-      = link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
-        = icon('rss')
-
-.gray-content-block.second-block
-  .user-calendar
-    %h4.center.light
-      %i.fa.fa-spinner.fa-spin
-  .user-calendar-activities
-
+%div{ class: container_class }
+  .tab-content
+    .tab-pane.active#activity
+      .gray-content-block.white.second-block
+        %div{ class: container_class }
+          .user-calendar
+            %h4.center.light
+              %i.fa.fa-spinner.fa-spin
+        .user-calendar-activities
 
-%ul.center-top-menu.no-top.no-bottom.bottom-border.wide
-  %li.active
-    = link_to "#activity", 'data-toggle' => 'tab' do
-      Activity
-  - if @groups.any?
-    %li
-      = link_to "#groups", 'data-toggle' => 'tab' do
-        Groups
-  - if @contributed_projects.present?
-    %li
-      = link_to "#contributed", 'data-toggle' => 'tab' do
-        Contributed projects
-  - if @projects.present?
-    %li
-      = link_to "#personal", 'data-toggle' => 'tab' do
-        Personal projects
 
-.tab-content
-  .tab-pane.active#activity
-    .content_list
-    = spinner
+      .content_list
+      = spinner
 
-  - if @groups.any?
-    .tab-pane#groups
-      %ul.content-list
-        - @groups.each do |group|
-          = render 'shared/groups/group', group: group
+    - if @groups.any?
+      .tab-pane#groups
+        %ul.content-list
+          - @groups.each do |group|
+            = render 'shared/groups/group', group: group
 
-  - if @contributed_projects.present?
-    .tab-pane#contributed
-      .contributed-projects
-        = render 'shared/projects/list',
-          projects: @contributed_projects.sort_by(&:star_count).reverse,
-          projects_limit: 5, stars: true, avatar: true
+    - if @contributed_projects.present?
+      .tab-pane#contributed
+        .contributed-projects
+          = render 'shared/projects/list',
+            projects: @contributed_projects.sort_by(&:star_count).reverse,
+            projects_limit: 10, stars: true, avatar: true
 
-  - if @projects.present?
-    .tab-pane#personal
-      .personal-projects
-        = render 'shared/projects/list',
-          projects: @projects.sort_by(&:star_count).reverse,
-          projects_limit: 10, stars: true, avatar: true
+    - if @projects.present?
+      .tab-pane#personal
+        .personal-projects
+          = render 'shared/projects/list',
+            projects: @projects.sort_by(&:star_count).reverse,
+            projects_limit: 10, stars: true, avatar: true
 
 :javascript
   $(".user-calendar").load("#{user_calendar_path}");
diff --git a/bin/background_jobs b/bin/background_jobs
index 5c85fb339e65c76eeeaa8b53c2b87565d63aa44e..1f67d73294965bfee3252f968c20b4d271b3b7b8 100755
--- a/bin/background_jobs
+++ b/bin/background_jobs
@@ -27,17 +27,17 @@ restart()
     stop
   fi
   killall
-  start_sidekiq -d -L $sidekiq_logfile
+  start_sidekiq -d -L $sidekiq_logfile >> $sidekiq_logfile 2>&1
 }
 
 start_no_deamonize()
 {
-  start_sidekiq
+  start_sidekiq >> $sidekiq_logfile 2>&1
 }
 
 start_sidekiq()
 {
-  bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile $@ >> $sidekiq_logfile 2>&1
+  bundle exec sidekiq -q post_receive -q mailers -q archive_repo -q system_hook -q project_web_hook -q gitlab_shell -q incoming_email -q runner -q common -q default -e $RAILS_ENV -P $sidekiq_pidfile "$@"
 }
 
 load_ok()
@@ -66,6 +66,9 @@ case "$1" in
   start_no_deamonize)
     start_no_deamonize
     ;;
+  start_foreground)
+    start_sidekiq
+    ;;
   restart)
     restart
     ;;
diff --git a/bin/web b/bin/web
index 67f236eb0bb67005d69c99164629dfeb914f9d36..03fe7a6354b6136cd7d5363d00a6056dae9d8ce1 100755
--- a/bin/web
+++ b/bin/web
@@ -5,6 +5,7 @@ app_root=$(pwd)
 
 unicorn_pidfile="$app_root/tmp/pids/unicorn.pid"
 unicorn_config="$app_root/config/unicorn.rb"
+unicorn_cmd="bundle exec unicorn_rails -c $unicorn_config -E $RAILS_ENV"
 
 get_unicorn_pid()
 {
@@ -18,7 +19,12 @@ get_unicorn_pid()
 
 start()
 {
-  bundle exec unicorn_rails -D -c $unicorn_config -E $RAILS_ENV
+  $unicorn_cmd -D
+}
+
+start_foreground()
+{
+  $unicorn_cmd
 }
 
 stop()
@@ -37,6 +43,9 @@ case "$1" in
   start)
     start
     ;;
+  start_foreground)
+    start_foreground
+    ;;
   stop)
     stop
     ;;
diff --git a/config.ru b/config.ru
index a2525c813618ca8500a12c8009618f6fe1fd7bd3..065ce59932f2ff8c832589bd20e19ae4defc4394 100644
--- a/config.ru
+++ b/config.ru
@@ -7,8 +7,11 @@ if defined?(Unicorn)
     # Unicorn self-process killer
     require 'unicorn/worker_killer'
 
+    min = (ENV['GITLAB_UNICORN_MEMORY_MIN'] || 300 * 1 << 20).to_i
+    max = (ENV['GITLAB_UNICORN_MEMORY_MAX'] || 350 * 1 << 20).to_i
+
     # Max memory size (RSS) per worker
-    use Unicorn::WorkerKiller::Oom, (200 * (1 << 20)), (250 * (1 << 20))
+    use Unicorn::WorkerKiller::Oom, min, max
   end
 end
 
diff --git a/config/environments/development.rb b/config/environments/development.rb
index c22722c606bd8cd6f30c869a425223a71823dc30..689694a348019fd441323644747f82eb1286493e 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -16,6 +16,9 @@ Rails.application.configure do
   # Print deprecation notices to the Rails logger
   config.active_support.deprecation = :log
 
+  # Raise an error on page load if there are pending migrations
+  config.active_record.migration_error = :page_load
+
   # Only use best-standards-support built into browsers
   config.action_dispatch.best_standards_support = :builtin
 
@@ -34,6 +37,8 @@ Rails.application.configure do
   config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }
   # Open sent mails in browser
   config.action_mailer.delivery_method = :letter_opener
+  # Don't make a mess when bootstrapping a development environment
+  config.action_mailer.perform_deliveries = (ENV['BOOTSTRAP'] != '1')
 
   config.eager_load = false
 end
diff --git a/config/gitlab.yml.example b/config/gitlab.yml.example
index 2d9f730c1831e6de76b8d297089b484449588553..d6e2c9380a5e3f0b288b5611da101ba7594dda11 100644
--- a/config/gitlab.yml.example
+++ b/config/gitlab.yml.example
@@ -204,6 +204,11 @@ production: &base
         bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
         password: '_the_password_of_the_bind_user'
 
+        # Set a timeout, in seconds, for LDAP queries. This helps avoid blocking
+        # a request if the LDAP server becomes unresponsive.
+        # A value of 0 means there is no timeout.
+        timeout: 10
+
         # This setting specifies if LDAP server is Active Directory LDAP server.
         # For non AD servers it skips the AD specific queries.
         # If your LDAP server is not AD, set this to false.
diff --git a/config/initializers/1_settings.rb b/config/initializers/1_settings.rb
index 4fbd84ee8901067b0de09c27cc6d025e1f63d352..04a7c16ebdec1d121bc47cded1b6a0ab63b39057 100644
--- a/config/initializers/1_settings.rb
+++ b/config/initializers/1_settings.rb
@@ -9,13 +9,8 @@ class Settings < Settingslogic
       gitlab.port.to_i == (gitlab.https ? 443 : 80)
     end
 
-    # get host without www, thanks to http://stackoverflow.com/a/6674363/1233435
-    def get_host_without_www(url)
-      url = URI.encode(url)
-      uri = URI.parse(url)
-      uri = URI.parse("http://#{url}") if uri.scheme.nil?
-      host = uri.host.downcase
-      host.start_with?('www.') ? host[4..-1] : host
+    def host_without_www(url)
+      host(url).sub('www.', '')
     end
 
     def build_gitlab_ci_url
@@ -87,6 +82,17 @@ class Settings < Settingslogic
         custom_port
       ]
     end
+
+    # Extract the host part of the given +url+.
+    def host(url)
+      url = url.downcase
+      url = "http://#{url}" unless url.start_with?('http')
+
+      # Get rid of the path so that we don't even have to encode it
+      url_without_path = url.sub(%r{(https?://[^\/]+)/?.*}, '\1')
+
+      URI.parse(url_without_path).host
+    end
   end
 end
 
@@ -108,6 +114,7 @@ if Settings.ldap['enabled'] || Rails.env.test?
 
   Settings.ldap['servers'].each do |key, server|
     server['label'] ||= 'LDAP'
+    server['timeout'] ||= 10.seconds
     server['block_auto_created_users'] = false if server['block_auto_created_users'].nil?
     server['allow_username_or_email_login'] = false if server['allow_username_or_email_login'].nil?
     server['active_directory'] = true if server['active_directory'].nil?
@@ -227,7 +234,7 @@ Settings['gravatar'] ||= Settingslogic.new({})
 Settings.gravatar['enabled']      = true if Settings.gravatar['enabled'].nil?
 Settings.gravatar['plain_url']  ||= 'http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
 Settings.gravatar['ssl_url']    ||= 'https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon'
-Settings.gravatar['host']         = Settings.get_host_without_www(Settings.gravatar['plain_url'])
+Settings.gravatar['host']         = Settings.host_without_www(Settings.gravatar['plain_url'])
 
 #
 # Cron Jobs
diff --git a/config/initializers/metrics.rb b/config/initializers/metrics.rb
index 52ace27b7ae231ebe94322152946bf2234afc3ef..0945b93ed5d94331e8c3d464d10b0c29e676fee2 100644
--- a/config/initializers/metrics.rb
+++ b/config/initializers/metrics.rb
@@ -49,12 +49,19 @@ if Gitlab::Metrics.enabled?
     config.instrument_instance_methods(Gitlab::Shell)
 
     config.instrument_methods(Gitlab::Git)
+    config.instrument_instance_methods(Gitlab::Git::Repository)
 
     Gitlab::Git.constants.each do |name|
       const = Gitlab::Git.const_get(name)
 
       config.instrument_methods(const) if const.is_a?(Module)
     end
+
+    Dir[Rails.root.join('app', 'finders', '*.rb')].each do |path|
+      const = File.basename(path, '.rb').camelize.constantize
+
+      config.instrument_instance_methods(const)
+    end
   end
 
   GC::Profiler.enable
diff --git a/config/initializers/sentry.rb b/config/initializers/sentry.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d0630b9fa0789ba6daf52511475dd843685cea22
--- /dev/null
+++ b/config/initializers/sentry.rb
@@ -0,0 +1,19 @@
+# Be sure to restart your server when you modify this file.
+
+require 'gitlab/current_settings'
+include Gitlab::CurrentSettings
+
+if Rails.env.production?
+  # allow it to fail: it may do so when create_from_defaults is executed before migrations are actually done
+  begin
+    sentry_enabled = current_application_settings.sentry_enabled
+  rescue
+    sentry_enabled = false
+  end
+
+  if sentry_enabled
+    Raven.configure do |config|
+      config.dsn = current_application_settings.sentry_dsn
+    end
+  end
+end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index f6cfb5efd2ada02443fe7939ad58f9b5922d719e..cedb5e207bd3798f0ff2a8e6651937ba368d2b8a 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -8,3 +8,7 @@ en:
       wrong_size: "is the wrong size (should be %{file_size})"
       size_too_small: "is too small (should be at least %{file_size})"
       size_too_big: "is too big (should be at most %{file_size})"
+  views:
+    pagination:
+      previous: "Prev"
+      next: "Next"
diff --git a/config/routes.rb b/config/routes.rb
index 3d5c70987c8939f676666c8dc3dd0748689374d0..75418db8d2584fdad5ecee5323ec663d53ccfdfa 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -88,6 +88,12 @@ Rails.application.routes.draw do
     end
   end
 
+  resources :sent_notifications, only: [], constraints: { id: /\h{32}/ } do
+    member do
+      get :unsubscribe
+    end
+  end
+
   # Spam reports
   resources :abuse_reports, only: [:new, :create]
 
@@ -219,7 +225,7 @@ Rails.application.routes.draw do
       get :test
     end
 
-    resources :broadcast_messages, only: [:index, :create, :destroy]
+    resources :broadcast_messages, only: [:index, :edit, :create, :update, :destroy]
     resource :logs, only: [:show]
     resource :background_jobs, controller: 'background_jobs', only: [:show]
 
@@ -513,7 +519,7 @@ Rails.application.routes.draw do
           end
         end
 
-        WIKI_SLUG_ID = { id: /[a-zA-Z.0-9_\-\/]+/ } unless defined? WIKI_SLUG_ID
+        WIKI_SLUG_ID = { id: /\S+/ } unless defined? WIKI_SLUG_ID
 
         scope do
           # Order matters to give priority to these matches
@@ -604,9 +610,14 @@ Rails.application.routes.draw do
           member do
             get :status
             post :cancel
-            get :download
             post :retry
           end
+
+          resource :artifacts, only: [] do
+            get :download
+            get :browse, path: 'browse(/*path)', format: false
+            get :file, path: 'file/*path', format: false
+          end
         end
 
         resources :hooks, only: [:index, :create, :destroy], constraints: { id: /\d+/ } do
diff --git a/db/fixtures/development/14_builds.rb b/db/fixtures/development/14_builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..03a123238451f8a65abcda2f78dd0418ad01e274
--- /dev/null
+++ b/db/fixtures/development/14_builds.rb
@@ -0,0 +1,79 @@
+class Gitlab::Seeder::Builds
+  BUILD_STATUSES = %w(running pending success failed canceled)
+
+  def initialize(project)
+    @project = project
+  end
+
+  def seed!
+    ci_commits.each do |ci_commit|
+      build = Ci::Build.new(build_attributes_for(ci_commit))
+
+      artifacts_cache_file(artifacts_archive_path) do |file|
+        build.artifacts_file = file
+      end
+
+      artifacts_cache_file(artifacts_metadata_path) do |file|
+        build.artifacts_metadata = file
+      end
+
+      begin
+        build.save!
+        print '.'
+      rescue ActiveRecord::RecordInvalid
+        print 'F'
+      end
+    end
+  end
+
+  def ci_commits
+    commits = @project.repository.commits('master', nil, 5)
+    commits_sha = commits.map { |commit| commit.raw.id }
+    commits_sha.map do |sha|
+      @project.ensure_ci_commit(sha)
+    end
+  rescue
+    []
+  end
+
+  def build_attributes_for(ci_commit)
+    { name: 'test build', commands: "$ build command",
+      stage: 'test', stage_idx: 1, ref: 'master',
+      user_id: build_user, gl_project_id: @project.id,
+      status: build_status, commit_id: ci_commit.id,
+      created_at: Time.now, updated_at: Time.now }
+  end
+
+  def build_user
+    @project.team.users.sample
+  end
+
+  def build_status
+    BUILD_STATUSES.sample
+  end
+
+  def artifacts_archive_path
+    Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
+  end
+
+  def artifacts_metadata_path
+    Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
+
+  end
+
+  def artifacts_cache_file(file_path)
+    cache_path = file_path.to_s.gsub('ci_', "p#{@project.id}_")
+
+    FileUtils.copy(file_path, cache_path)
+    File.open(cache_path) do |file|
+      yield file
+    end
+  end
+end
+
+Gitlab::Seeder.quiet do
+  Project.all.sample(10).each do |project|
+    project_builds = Gitlab::Seeder::Builds.new(project)
+    project_builds.seed!
+  end
+end
diff --git a/db/migrate/20151201203948_raise_hook_url_limit.rb b/db/migrate/20151201203948_raise_hook_url_limit.rb
new file mode 100644
index 0000000000000000000000000000000000000000..98a7fca6f6f3eb256ffd78c312a72dd244361d8e
--- /dev/null
+++ b/db/migrate/20151201203948_raise_hook_url_limit.rb
@@ -0,0 +1,5 @@
+class RaiseHookUrlLimit < ActiveRecord::Migration
+  def change
+    change_column :web_hooks, :url, :string, limit: 2000
+  end
+end
diff --git a/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6c282fc50394eb6daa761c1344acd338c94848d5
--- /dev/null
+++ b/db/migrate/20151230132518_add_artifacts_metadata_to_ci_build.rb
@@ -0,0 +1,5 @@
+class AddArtifactsMetadataToCiBuild < ActiveRecord::Migration
+  def change
+    add_column :ci_builds, :artifacts_metadata, :text
+  end
+end
diff --git a/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb b/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb
new file mode 100644
index 0000000000000000000000000000000000000000..78fdfeaf5cf9a2ef0cbc90b3e043970cf3c4c077
--- /dev/null
+++ b/db/migrate/20151231202530_remove_alert_type_from_broadcast_messages.rb
@@ -0,0 +1,5 @@
+class RemoveAlertTypeFromBroadcastMessages < ActiveRecord::Migration
+  def change
+    remove_column :broadcast_messages, :alert_type, :integer
+  end
+end
diff --git a/db/migrate/20160113111034_add_metrics_sample_interval.rb b/db/migrate/20160113111034_add_metrics_sample_interval.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b741f5d2c758db6ff093b3d55e995458f8d50fbb
--- /dev/null
+++ b/db/migrate/20160113111034_add_metrics_sample_interval.rb
@@ -0,0 +1,6 @@
+class AddMetricsSampleInterval < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :metrics_sample_interval, :integer,
+      default: 15
+  end
+end
diff --git a/db/migrate/20160118155830_add_sentry_to_application_settings.rb b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fa7ff9d92289c54297abc4f1424f21ea0d3b5a03
--- /dev/null
+++ b/db/migrate/20160118155830_add_sentry_to_application_settings.rb
@@ -0,0 +1,8 @@
+class AddSentryToApplicationSettings < ActiveRecord::Migration
+  def change
+    change_table :application_settings do |t|
+      t.boolean :sentry_enabled, default: false
+      t.string :sentry_dsn
+    end
+  end
+end
diff --git a/db/migrate/20160118232755_add_ip_blocking_settings_to_application_settings.rb b/db/migrate/20160118232755_add_ip_blocking_settings_to_application_settings.rb
new file mode 100644
index 0000000000000000000000000000000000000000..26606b10b54d66a6d02090892c9f34d6afefdeaa
--- /dev/null
+++ b/db/migrate/20160118232755_add_ip_blocking_settings_to_application_settings.rb
@@ -0,0 +1,6 @@
+class AddIpBlockingSettingsToApplicationSettings < ActiveRecord::Migration
+  def change
+    add_column :application_settings, :ip_blocking_enabled, :boolean, default: false
+    add_column :application_settings, :dnsbl_servers_list, :text
+  end
+end
diff --git a/db/migrate/20160119111158_add_services_category.rb b/db/migrate/20160119111158_add_services_category.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a9110a8418b5b0394710958535fe82c2acb69cc3
--- /dev/null
+++ b/db/migrate/20160119111158_add_services_category.rb
@@ -0,0 +1,39 @@
+class AddServicesCategory < ActiveRecord::Migration
+  def up
+    add_column :services, :category, :string, default: 'common', null: false
+
+    category = quote_column_name('category')
+    type     = quote_column_name('type')
+
+    execute <<-EOF
+UPDATE services
+SET #{category} = 'issue_tracker'
+WHERE #{type} IN (
+  'CustomIssueTrackerService',
+  'GitlabIssueTrackerService',
+  'IssueTrackerService',
+  'JiraService',
+  'RedmineService'
+);
+EOF
+
+    execute <<-EOF
+UPDATE services
+SET #{category} = 'ci'
+WHERE #{type} IN (
+  'BambooService',
+  'BuildkiteService',
+  'CiService',
+  'DroneCiService',
+  'GitlabCiService',
+  'TeamcityService'
+);
+    EOF
+
+    add_index :services, :category
+  end
+
+  def down
+    remove_column :services, :category
+  end
+end
diff --git a/db/migrate/20160119112418_add_services_default.rb b/db/migrate/20160119112418_add_services_default.rb
new file mode 100644
index 0000000000000000000000000000000000000000..69a42d7b873a4730f4998cb4b0789372472870b5
--- /dev/null
+++ b/db/migrate/20160119112418_add_services_default.rb
@@ -0,0 +1,20 @@
+class AddServicesDefault < ActiveRecord::Migration
+  def up
+    add_column :services, :default, :boolean, default: false
+
+    default = quote_column_name('default')
+    type    = quote_column_name('type')
+
+    execute <<-EOF
+UPDATE services
+SET #{default} = true
+WHERE #{type} = 'GitlabIssueTrackerService'
+EOF
+
+    add_index :services, :default
+  end
+
+  def down
+    remove_column :services, :default
+  end
+end
diff --git a/db/migrate/20160119145451_add_ldap_email_to_users.rb b/db/migrate/20160119145451_add_ldap_email_to_users.rb
new file mode 100644
index 0000000000000000000000000000000000000000..654d31ab15a6dcd7cf50d2eb34bfc78f35625b09
--- /dev/null
+++ b/db/migrate/20160119145451_add_ldap_email_to_users.rb
@@ -0,0 +1,30 @@
+class AddLdapEmailToUsers < ActiveRecord::Migration
+  def up
+    add_column :users, :ldap_email, :boolean, default: false, null: false
+
+    if Gitlab::Database.mysql?
+      execute %{
+        UPDATE users, identities
+        SET users.ldap_email = TRUE
+        WHERE identities.user_id = users.id
+        AND users.email LIKE 'temp-email-for-oauth%'
+        AND identities.provider LIKE 'ldap%'
+        AND identities.extern_uid IS NOT NULL
+      }
+    else
+      execute %{
+        UPDATE users
+        SET ldap_email = TRUE
+        FROM identities
+        WHERE identities.user_id = users.id
+        AND users.email LIKE 'temp-email-for-oauth%'
+        AND identities.provider LIKE 'ldap%'
+        AND identities.extern_uid IS NOT NULL
+      }
+    end
+  end
+
+  def down
+    remove_column :users, :ldap_email
+  end
+end
diff --git a/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d6c6aa4a4e83d112f6ff044f9549139644d4e69e
--- /dev/null
+++ b/db/migrate/20160120172143_add_base_commit_sha_to_merge_request_diffs.rb
@@ -0,0 +1,5 @@
+class AddBaseCommitShaToMergeRequestDiffs < ActiveRecord::Migration
+  def change
+    add_column :merge_request_diffs, :base_commit_sha, :string
+  end
+end
diff --git a/db/migrate/limits_to_mysql.rb b/db/migrate/limits_to_mysql.rb
index 2b7afae6d7cb3f73197f2c6d9549ba216429af82..14d7e84d856c5b2fe634d81566681e4ff08c69a1 100644
--- a/db/migrate/limits_to_mysql.rb
+++ b/db/migrate/limits_to_mysql.rb
@@ -6,5 +6,6 @@ class LimitsToMysql < ActiveRecord::Migration
     change_column :merge_request_diffs, :st_diffs, :text, limit: 2147483647
     change_column :snippets, :content, :text, limit: 2147483647
     change_column :notes, :st_diff, :text, limit: 2147483647
+    change_column :events, :data, :text, limit: 2147483647
   end
 end
diff --git a/db/schema.rb b/db/schema.rb
index 2ded8a45e188f523220a5d2eb34f520a61afbfd2..97594011a0269fe06c576e947eee9e6373917e44 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -11,7 +11,7 @@
 #
 # It's strongly recommended that you check this file into your version control system.
 
-ActiveRecord::Schema.define(version: 20160106164438) do
+ActiveRecord::Schema.define(version: 20160120172143) do
 
   # These are extensions that must be enabled in order to support this database
   enable_extension "plpgsql"
@@ -32,23 +32,23 @@ ActiveRecord::Schema.define(version: 20160106164438) do
     t.text     "sign_in_text"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "home_page_url",                     limit: 255
-    t.integer  "default_branch_protection",                     default: 2
-    t.boolean  "twitter_sharing_enabled",                       default: true
+    t.string   "home_page_url"
+    t.integer  "default_branch_protection",         default: 2
+    t.boolean  "twitter_sharing_enabled",           default: true
     t.text     "restricted_visibility_levels"
-    t.boolean  "version_check_enabled",                         default: true
-    t.integer  "max_attachment_size",                           default: 10,          null: false
+    t.boolean  "version_check_enabled",             default: true
+    t.integer  "max_attachment_size",               default: 10,          null: false
     t.integer  "default_project_visibility"
     t.integer  "default_snippet_visibility"
     t.text     "restricted_signup_domains"
-    t.boolean  "user_oauth_applications",                       default: true
-    t.string   "after_sign_out_path",               limit: 255
-    t.integer  "session_expire_delay",                          default: 10080,       null: false
+    t.boolean  "user_oauth_applications",           default: true
+    t.string   "after_sign_out_path"
+    t.integer  "session_expire_delay",              default: 10080,       null: false
     t.text     "import_sources"
     t.text     "help_page_text"
-    t.string   "admin_notification_email",          limit: 255
-    t.boolean  "shared_runners_enabled",                        default: true,        null: false
-    t.integer  "max_artifacts_size",                            default: 100,         null: false
+    t.string   "admin_notification_email"
+    t.boolean  "shared_runners_enabled",            default: true,        null: false
+    t.integer  "max_artifacts_size",                default: 100,         null: false
     t.string   "runners_registration_token"
     t.boolean  "require_two_factor_authentication", default: false
     t.integer  "two_factor_grace_period",           default: 48
@@ -60,14 +60,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do
     t.boolean  "recaptcha_enabled",                 default: false
     t.string   "recaptcha_site_key"
     t.string   "recaptcha_private_key"
-    t.integer  "metrics_port",                                  default: 8089
+    t.integer  "metrics_port",                      default: 8089
+    t.integer  "metrics_sample_interval",           default: 15
+    t.boolean  "sentry_enabled",                    default: false
+    t.string   "sentry_dsn"
+    t.boolean  "ip_blocking_enabled",               default: false
+    t.text     "dnsbl_servers_list"
   end
 
   create_table "audit_events", force: :cascade do |t|
-    t.integer  "author_id",               null: false
-    t.string   "type",        limit: 255, null: false
-    t.integer  "entity_id",               null: false
-    t.string   "entity_type", limit: 255, null: false
+    t.integer  "author_id",   null: false
+    t.string   "type",        null: false
+    t.integer  "entity_id",   null: false
+    t.string   "entity_type", null: false
     t.text     "details"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -78,14 +83,13 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "audit_events", ["type"], name: "index_audit_events_on_type", using: :btree
 
   create_table "broadcast_messages", force: :cascade do |t|
-    t.text     "message",                null: false
+    t.text     "message",    null: false
     t.datetime "starts_at"
     t.datetime "ends_at"
-    t.integer  "alert_type"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "color",      limit: 255
-    t.string   "font",       limit: 255
+    t.string   "color"
+    t.string   "font"
   end
 
   create_table "ci_application_settings", force: :cascade do |t|
@@ -97,7 +101,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do
 
   create_table "ci_builds", force: :cascade do |t|
     t.integer  "project_id"
-    t.string   "status",             limit: 255
+    t.string   "status"
     t.datetime "finished_at"
     t.text     "trace"
     t.datetime "created_at"
@@ -108,21 +112,22 @@ ActiveRecord::Schema.define(version: 20160106164438) do
     t.integer  "commit_id"
     t.text     "commands"
     t.integer  "job_id"
-    t.string   "name",               limit: 255
-    t.boolean  "deploy",                         default: false
+    t.string   "name"
+    t.boolean  "deploy",             default: false
     t.text     "options"
-    t.boolean  "allow_failure",                  default: false, null: false
-    t.string   "stage",              limit: 255
+    t.boolean  "allow_failure",      default: false, null: false
+    t.string   "stage"
     t.integer  "trigger_request_id"
     t.integer  "stage_idx"
     t.boolean  "tag"
-    t.string   "ref",                limit: 255
+    t.string   "ref"
     t.integer  "user_id"
-    t.string   "type",               limit: 255
-    t.string   "target_url",         limit: 255
-    t.string   "description",        limit: 255
+    t.string   "type"
+    t.string   "target_url"
+    t.string   "description"
     t.text     "artifacts_file"
     t.integer  "gl_project_id"
+    t.text     "artifacts_metadata"
   end
 
   add_index "ci_builds", ["commit_id", "stage_idx", "created_at"], name: "index_ci_builds_on_commit_id_and_stage_idx_and_created_at", using: :btree
@@ -139,13 +144,13 @@ ActiveRecord::Schema.define(version: 20160106164438) do
 
   create_table "ci_commits", force: :cascade do |t|
     t.integer  "project_id"
-    t.string   "ref",           limit: 255
-    t.string   "sha",           limit: 255
-    t.string   "before_sha",    limit: 255
+    t.string   "ref"
+    t.string   "sha"
+    t.string   "before_sha"
     t.text     "push_data"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.boolean  "tag",                       default: false
+    t.boolean  "tag",           default: false
     t.text     "yaml_errors"
     t.datetime "committed_at"
     t.integer  "gl_project_id"
@@ -172,16 +177,16 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "ci_events", ["project_id"], name: "index_ci_events_on_project_id", using: :btree
 
   create_table "ci_jobs", force: :cascade do |t|
-    t.integer  "project_id",                                      null: false
+    t.integer  "project_id",                          null: false
     t.text     "commands"
-    t.boolean  "active",                     default: true,       null: false
+    t.boolean  "active",         default: true,       null: false
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "name",           limit: 255
-    t.boolean  "build_branches",             default: true,       null: false
-    t.boolean  "build_tags",                 default: false,      null: false
-    t.string   "job_type",       limit: 255, default: "parallel"
-    t.string   "refs",           limit: 255
+    t.string   "name"
+    t.boolean  "build_branches", default: true,       null: false
+    t.boolean  "build_tags",     default: false,      null: false
+    t.string   "job_type",       default: "parallel"
+    t.string   "refs"
     t.datetime "deleted_at"
   end
 
@@ -189,25 +194,25 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "ci_jobs", ["project_id"], name: "index_ci_jobs_on_project_id", using: :btree
 
   create_table "ci_projects", force: :cascade do |t|
-    t.string   "name",                     limit: 255
-    t.integer  "timeout",                              default: 3600,  null: false
+    t.string   "name"
+    t.integer  "timeout",                  default: 3600,  null: false
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "token",                    limit: 255
-    t.string   "default_ref",              limit: 255
-    t.string   "path",                     limit: 255
-    t.boolean  "always_build",                         default: false, null: false
+    t.string   "token"
+    t.string   "default_ref"
+    t.string   "path"
+    t.boolean  "always_build",             default: false, null: false
     t.integer  "polling_interval"
-    t.boolean  "public",                               default: false, null: false
-    t.string   "ssh_url_to_repo",          limit: 255
+    t.boolean  "public",                   default: false, null: false
+    t.string   "ssh_url_to_repo"
     t.integer  "gitlab_id"
-    t.boolean  "allow_git_fetch",                      default: true,  null: false
-    t.string   "email_recipients",         limit: 255, default: "",    null: false
-    t.boolean  "email_add_pusher",                     default: true,  null: false
-    t.boolean  "email_only_broken_builds",             default: true,  null: false
-    t.string   "skip_refs",                limit: 255
-    t.string   "coverage_regex",           limit: 255
-    t.boolean  "shared_runners_enabled",               default: false
+    t.boolean  "allow_git_fetch",          default: true,  null: false
+    t.string   "email_recipients",         default: "",    null: false
+    t.boolean  "email_add_pusher",         default: true,  null: false
+    t.boolean  "email_only_broken_builds", default: true,  null: false
+    t.string   "skip_refs"
+    t.string   "coverage_regex"
+    t.boolean  "shared_runners_enabled",   default: false
     t.text     "generated_yaml_config"
   end
 
@@ -226,34 +231,34 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "ci_runner_projects", ["runner_id"], name: "index_ci_runner_projects_on_runner_id", using: :btree
 
   create_table "ci_runners", force: :cascade do |t|
-    t.string   "token",        limit: 255
+    t.string   "token"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "description",  limit: 255
+    t.string   "description"
     t.datetime "contacted_at"
-    t.boolean  "active",                   default: true,  null: false
-    t.boolean  "is_shared",                default: false
-    t.string   "name",         limit: 255
-    t.string   "version",      limit: 255
-    t.string   "revision",     limit: 255
-    t.string   "platform",     limit: 255
-    t.string   "architecture", limit: 255
+    t.boolean  "active",       default: true,  null: false
+    t.boolean  "is_shared",    default: false
+    t.string   "name"
+    t.string   "version"
+    t.string   "revision"
+    t.string   "platform"
+    t.string   "architecture"
   end
 
   create_table "ci_services", force: :cascade do |t|
-    t.string   "type",       limit: 255
-    t.string   "title",      limit: 255
-    t.integer  "project_id",                             null: false
+    t.string   "type"
+    t.string   "title"
+    t.integer  "project_id",                 null: false
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.boolean  "active",                 default: false, null: false
+    t.boolean  "active",     default: false, null: false
     t.text     "properties"
   end
 
   add_index "ci_services", ["project_id"], name: "index_ci_services_on_project_id", using: :btree
 
   create_table "ci_sessions", force: :cascade do |t|
-    t.string   "session_id", limit: 255, null: false
+    t.string   "session_id", null: false
     t.text     "data"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -265,9 +270,9 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "ci_taggings", force: :cascade do |t|
     t.integer  "tag_id"
     t.integer  "taggable_id"
-    t.string   "taggable_type", limit: 255
+    t.string   "taggable_type"
     t.integer  "tagger_id"
-    t.string   "tagger_type",   limit: 255
+    t.string   "tagger_type"
     t.string   "context",       limit: 128
     t.datetime "created_at"
   end
@@ -276,8 +281,8 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   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: :cascade do |t|
-    t.string  "name",           limit: 255
-    t.integer "taggings_count",             default: 0
+    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
@@ -291,7 +296,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   end
 
   create_table "ci_triggers", force: :cascade do |t|
-    t.string   "token",         limit: 255
+    t.string   "token"
     t.integer  "project_id"
     t.datetime "deleted_at"
     t.datetime "created_at"
@@ -304,19 +309,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do
 
   create_table "ci_variables", force: :cascade do |t|
     t.integer "project_id"
-    t.string  "key",                  limit: 255
+    t.string  "key"
     t.text    "value"
     t.text    "encrypted_value"
-    t.string  "encrypted_value_salt", limit: 255
-    t.string  "encrypted_value_iv",   limit: 255
+    t.string  "encrypted_value_salt"
+    t.string  "encrypted_value_iv"
     t.integer "gl_project_id"
   end
 
   add_index "ci_variables", ["gl_project_id"], name: "index_ci_variables_on_gl_project_id", using: :btree
 
   create_table "ci_web_hooks", force: :cascade do |t|
-    t.string   "url",        limit: 255, null: false
-    t.integer  "project_id",             null: false
+    t.string   "url",        null: false
+    t.integer  "project_id", null: false
     t.datetime "created_at"
     t.datetime "updated_at"
   end
@@ -331,8 +336,8 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "deploy_keys_projects", ["project_id"], name: "index_deploy_keys_projects_on_project_id", using: :btree
 
   create_table "emails", force: :cascade do |t|
-    t.integer  "user_id",                null: false
-    t.string   "email",      limit: 255, null: false
+    t.integer  "user_id",    null: false
+    t.string   "email",      null: false
     t.datetime "created_at"
     t.datetime "updated_at"
   end
@@ -341,9 +346,9 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "emails", ["user_id"], name: "index_emails_on_user_id", using: :btree
 
   create_table "events", force: :cascade do |t|
-    t.string   "target_type", limit: 255
+    t.string   "target_type"
     t.integer  "target_id"
-    t.string   "title",       limit: 255
+    t.string   "title"
     t.text     "data"
     t.integer  "project_id"
     t.datetime "created_at"
@@ -369,8 +374,8 @@ ActiveRecord::Schema.define(version: 20160106164438) 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: :cascade do |t|
-    t.string   "extern_uid", limit: 255
-    t.string   "provider",   limit: 255
+    t.string   "extern_uid"
+    t.string   "provider"
     t.integer  "user_id"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -380,17 +385,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "identities", ["user_id"], name: "index_identities_on_user_id", using: :btree
 
   create_table "issues", force: :cascade do |t|
-    t.string   "title",         limit: 255
+    t.string   "title"
     t.integer  "assignee_id"
     t.integer  "author_id"
     t.integer  "project_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.integer  "position",                  default: 0
-    t.string   "branch_name",   limit: 255
+    t.integer  "position",      default: 0
+    t.string   "branch_name"
     t.text     "description"
     t.integer  "milestone_id"
-    t.string   "state",         limit: 255
+    t.string   "state"
     t.integer  "iid"
     t.integer  "updated_by_id"
   end
@@ -410,10 +415,10 @@ ActiveRecord::Schema.define(version: 20160106164438) do
     t.datetime "created_at"
     t.datetime "updated_at"
     t.text     "key"
-    t.string   "title",       limit: 255
-    t.string   "type",        limit: 255
-    t.string   "fingerprint", limit: 255
-    t.boolean  "public",                  default: false, null: false
+    t.string   "title"
+    t.string   "type"
+    t.string   "fingerprint"
+    t.boolean  "public",      default: false, null: false
   end
 
   add_index "keys", ["created_at", "id"], name: "index_keys_on_created_at_and_id", using: :btree
@@ -422,7 +427,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "label_links", force: :cascade do |t|
     t.integer  "label_id"
     t.integer  "target_id"
-    t.string   "target_type", limit: 255
+    t.string   "target_type"
     t.datetime "created_at"
     t.datetime "updated_at"
   end
@@ -431,22 +436,22 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "label_links", ["target_id", "target_type"], name: "index_label_links_on_target_id_and_target_type", using: :btree
 
   create_table "labels", force: :cascade do |t|
-    t.string   "title",      limit: 255
-    t.string   "color",      limit: 255
+    t.string   "title"
+    t.string   "color"
     t.integer  "project_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.boolean  "template",               default: false
+    t.boolean  "template",   default: false
   end
 
   add_index "labels", ["project_id"], name: "index_labels_on_project_id", using: :btree
 
   create_table "lfs_objects", force: :cascade do |t|
-    t.string   "oid",        limit: 255, null: false
-    t.integer  "size",                   null: false
+    t.string   "oid",        null: false
+    t.integer  "size",       null: false
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "file",       limit: 255
+    t.string   "file"
   end
 
   add_index "lfs_objects", ["oid"], name: "index_lfs_objects_on_oid", unique: true, using: :btree
@@ -461,17 +466,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   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",        limit: 255, null: false
+    t.integer  "access_level",       null: false
+    t.integer  "source_id",          null: false
+    t.string   "source_type",        null: false
     t.integer  "user_id"
-    t.integer  "notification_level",             null: false
-    t.string   "type",               limit: 255
+    t.integer  "notification_level", null: false
+    t.string   "type"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "created_by_id"
-    t.string   "invite_email",       limit: 255
-    t.string   "invite_token",       limit: 255
+    t.string   "invite_email"
+    t.string   "invite_token"
     t.datetime "invite_accepted_at"
   end
 
@@ -483,37 +488,38 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "members", ["user_id"], name: "index_members_on_user_id", using: :btree
 
   create_table "merge_request_diffs", force: :cascade do |t|
-    t.string   "state",            limit: 255
+    t.string   "state"
     t.text     "st_commits"
     t.text     "st_diffs"
-    t.integer  "merge_request_id",             null: false
+    t.integer  "merge_request_id", null: false
     t.datetime "created_at"
     t.datetime "updated_at"
+    t.string   "base_commit_sha"
   end
 
   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: :cascade do |t|
-    t.string   "target_branch",             limit: 255,                 null: false
-    t.string   "source_branch",             limit: 255,                 null: false
-    t.integer  "source_project_id",                                     null: false
+    t.string   "target_branch",                             null: false
+    t.string   "source_branch",                             null: false
+    t.integer  "source_project_id",                         null: false
     t.integer  "author_id"
     t.integer  "assignee_id"
-    t.string   "title",                     limit: 255
+    t.string   "title"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "milestone_id"
-    t.string   "state",                     limit: 255
-    t.string   "merge_status",              limit: 255
-    t.integer  "target_project_id",                                     null: false
+    t.string   "state"
+    t.string   "merge_status"
+    t.integer  "target_project_id",                         null: false
     t.integer  "iid"
     t.text     "description"
-    t.integer  "position",                              default: 0
+    t.integer  "position",                  default: 0
     t.datetime "locked_at"
     t.integer  "updated_by_id"
-    t.string   "merge_error",               limit: 255
+    t.string   "merge_error"
     t.text     "merge_params"
-    t.boolean  "merge_when_build_succeeds",             default: false, null: false
+    t.boolean  "merge_when_build_succeeds", default: false, null: false
     t.integer  "merge_user_id"
   end
 
@@ -529,13 +535,13 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "merge_requests", ["title"], name: "index_merge_requests_on_title", using: :btree
 
   create_table "milestones", force: :cascade do |t|
-    t.string   "title",       limit: 255, null: false
-    t.integer  "project_id",              null: false
+    t.string   "title",       null: false
+    t.integer  "project_id",  null: false
     t.text     "description"
     t.date     "due_date"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "state",       limit: 255
+    t.string   "state"
     t.integer  "iid"
   end
 
@@ -543,16 +549,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "milestones", ["due_date"], name: "index_milestones_on_due_date", using: :btree
   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
+  add_index "milestones", ["title"], name: "index_milestones_on_title", using: :btree
 
   create_table "namespaces", force: :cascade do |t|
-    t.string   "name",        limit: 255,              null: false
-    t.string   "path",        limit: 255,              null: false
+    t.string   "name",                     null: false
+    t.string   "path",                     null: false
     t.integer  "owner_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "type",        limit: 255
-    t.string   "description", limit: 255, default: "", null: false
-    t.string   "avatar",      limit: 255
+    t.string   "type"
+    t.string   "description", default: "", null: false
+    t.string   "avatar"
   end
 
   add_index "namespaces", ["created_at", "id"], name: "index_namespaces_on_created_at_and_id", using: :btree
@@ -563,19 +570,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do
 
   create_table "notes", force: :cascade do |t|
     t.text     "note"
-    t.string   "noteable_type", limit: 255
+    t.string   "noteable_type"
     t.integer  "author_id"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "project_id"
-    t.string   "attachment",    limit: 255
-    t.string   "line_code",     limit: 255
-    t.string   "commit_id",     limit: 255
+    t.string   "attachment"
+    t.string   "line_code"
+    t.string   "commit_id"
     t.integer  "noteable_id"
-    t.boolean  "system",                    default: false, null: false
+    t.boolean  "system",        default: false, null: false
     t.text     "st_diff"
     t.integer  "updated_by_id"
-    t.boolean  "is_award",                  default: false, null: false
+    t.boolean  "is_award",      default: false, null: false
   end
 
   add_index "notes", ["author_id"], name: "index_notes_on_author_id", using: :btree
@@ -591,14 +598,14 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "notes", ["updated_at"], name: "index_notes_on_updated_at", using: :btree
 
   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",             limit: 255, null: false
-    t.integer  "expires_in",                    null: false
-    t.text     "redirect_uri",                  null: false
-    t.datetime "created_at",                    null: false
+    t.integer  "resource_owner_id", null: false
+    t.integer  "application_id",    null: false
+    t.string   "token",             null: false
+    t.integer  "expires_in",        null: false
+    t.text     "redirect_uri",      null: false
+    t.datetime "created_at",        null: false
     t.datetime "revoked_at"
-    t.string   "scopes",            limit: 255
+    t.string   "scopes"
   end
 
   add_index "oauth_access_grants", ["token"], name: "index_oauth_access_grants_on_token", unique: true, using: :btree
@@ -606,12 +613,12 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "oauth_access_tokens", force: :cascade do |t|
     t.integer  "resource_owner_id"
     t.integer  "application_id"
-    t.string   "token",             limit: 255, null: false
-    t.string   "refresh_token",     limit: 255
+    t.string   "token",             null: false
+    t.string   "refresh_token"
     t.integer  "expires_in"
     t.datetime "revoked_at"
-    t.datetime "created_at",                    null: false
-    t.string   "scopes",            limit: 255
+    t.datetime "created_at",        null: false
+    t.string   "scopes"
   end
 
   add_index "oauth_access_tokens", ["refresh_token"], name: "index_oauth_access_tokens_on_refresh_token", unique: true, using: :btree
@@ -619,15 +626,15 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "oauth_access_tokens", ["token"], name: "index_oauth_access_tokens_on_token", unique: true, using: :btree
 
   create_table "oauth_applications", force: :cascade do |t|
-    t.string   "name",         limit: 255,              null: false
-    t.string   "uid",          limit: 255,              null: false
-    t.string   "secret",       limit: 255,              null: false
-    t.text     "redirect_uri",                          null: false
-    t.string   "scopes",       limit: 255, default: "", null: false
+    t.string   "name",                      null: false
+    t.string   "uid",                       null: false
+    t.string   "secret",                    null: false
+    t.text     "redirect_uri",              null: false
+    t.string   "scopes",       default: "", null: false
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "owner_id"
-    t.string   "owner_type",   limit: 255
+    t.string   "owner_type"
   end
 
   add_index "oauth_applications", ["owner_id", "owner_type"], name: "index_oauth_applications_on_owner_id_and_owner_type", using: :btree
@@ -639,39 +646,39 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   end
 
   create_table "projects", force: :cascade do |t|
-    t.string   "name",                   limit: 255
-    t.string   "path",                   limit: 255
+    t.string   "name"
+    t.string   "path"
     t.text     "description"
     t.datetime "created_at"
     t.datetime "updated_at"
     t.integer  "creator_id"
-    t.boolean  "issues_enabled",                     default: true,     null: false
-    t.boolean  "wall_enabled",                       default: true,     null: false
-    t.boolean  "merge_requests_enabled",             default: true,     null: false
-    t.boolean  "wiki_enabled",                       default: true,     null: false
+    t.boolean  "issues_enabled",         default: true,     null: false
+    t.boolean  "wall_enabled",           default: true,     null: false
+    t.boolean  "merge_requests_enabled", default: true,     null: false
+    t.boolean  "wiki_enabled",           default: true,     null: false
     t.integer  "namespace_id"
-    t.string   "issues_tracker",         limit: 255, default: "gitlab", null: false
-    t.string   "issues_tracker_id",      limit: 255
-    t.boolean  "snippets_enabled",                   default: true,     null: false
+    t.string   "issues_tracker",         default: "gitlab", null: false
+    t.string   "issues_tracker_id"
+    t.boolean  "snippets_enabled",       default: true,     null: false
     t.datetime "last_activity_at"
-    t.string   "import_url",             limit: 255
-    t.integer  "visibility_level",                   default: 0,        null: false
-    t.boolean  "archived",                           default: false,    null: false
-    t.string   "avatar",                 limit: 255
-    t.string   "import_status",          limit: 255
-    t.float    "repository_size",                    default: 0.0
-    t.integer  "star_count",                         default: 0,        null: false
-    t.string   "import_type",            limit: 255
-    t.string   "import_source",          limit: 255
-    t.integer  "commit_count",                       default: 0
+    t.string   "import_url"
+    t.integer  "visibility_level",       default: 0,        null: false
+    t.boolean  "archived",               default: false,    null: false
+    t.string   "avatar"
+    t.string   "import_status"
+    t.float    "repository_size",        default: 0.0
+    t.integer  "star_count",             default: 0,        null: false
+    t.string   "import_type"
+    t.string   "import_source"
+    t.integer  "commit_count",           default: 0
     t.text     "import_error"
     t.integer  "ci_id"
-    t.boolean  "builds_enabled",                     default: true,     null: false
-    t.boolean  "shared_runners_enabled",             default: true,     null: false
+    t.boolean  "builds_enabled",         default: true,     null: false
+    t.boolean  "shared_runners_enabled", default: true,     null: false
     t.string   "runners_token"
     t.string   "build_coverage_regex"
-    t.boolean  "build_allow_git_fetch",              default: true,     null: false
-    t.integer  "build_timeout",                      default: 3600,     null: false
+    t.boolean  "build_allow_git_fetch",  default: true,     null: false
+    t.integer  "build_timeout",          default: 3600,     null: false
   end
 
   add_index "projects", ["builds_enabled", "shared_runners_enabled"], name: "index_projects_on_builds_enabled_and_shared_runners_enabled", using: :btree
@@ -687,17 +694,17 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "projects", ["visibility_level"], name: "index_projects_on_visibility_level", using: :btree
 
   create_table "protected_branches", force: :cascade do |t|
-    t.integer  "project_id",                                      null: false
-    t.string   "name",                limit: 255,                 null: false
+    t.integer  "project_id",                          null: false
+    t.string   "name",                                null: false
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.boolean  "developers_can_push",             default: false, null: false
+    t.boolean  "developers_can_push", default: false, null: false
   end
 
   add_index "protected_branches", ["project_id"], name: "index_protected_branches_on_project_id", using: :btree
 
   create_table "releases", force: :cascade do |t|
-    t.string   "tag",         limit: 255
+    t.string   "tag"
     t.text     "description"
     t.integer  "project_id"
     t.datetime "created_at"
@@ -710,47 +717,51 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "sent_notifications", force: :cascade do |t|
     t.integer "project_id"
     t.integer "noteable_id"
-    t.string  "noteable_type", limit: 255
+    t.string  "noteable_type"
     t.integer "recipient_id"
-    t.string  "commit_id",     limit: 255
-    t.string  "reply_key",     limit: 255, null: false
-    t.string  "line_code",     limit: 255
+    t.string  "commit_id"
+    t.string  "reply_key",     null: false
+    t.string  "line_code"
   end
 
   add_index "sent_notifications", ["reply_key"], name: "index_sent_notifications_on_reply_key", unique: true, using: :btree
 
   create_table "services", force: :cascade do |t|
-    t.string   "type",                  limit: 255
-    t.string   "title",                 limit: 255
+    t.string   "type"
+    t.string   "title"
     t.integer  "project_id"
-    t.datetime "created_at"
-    t.datetime "updated_at"
-    t.boolean  "active",                            default: false, null: false
+    t.datetime "created_at",                               null: false
+    t.datetime "updated_at",                               null: false
+    t.boolean  "active",                                   null: false
     t.text     "properties"
-    t.boolean  "template",                          default: false
-    t.boolean  "push_events",                       default: true
-    t.boolean  "issues_events",                     default: true
-    t.boolean  "merge_requests_events",             default: true
-    t.boolean  "tag_push_events",                   default: true
-    t.boolean  "note_events",                       default: true,  null: false
-    t.boolean  "build_events",                      default: false, null: false
-  end
-
+    t.boolean  "template",              default: false
+    t.boolean  "push_events",           default: true
+    t.boolean  "issues_events",         default: true
+    t.boolean  "merge_requests_events", default: true
+    t.boolean  "tag_push_events",       default: true
+    t.boolean  "note_events",           default: true,     null: false
+    t.boolean  "build_events",          default: false,    null: false
+    t.string   "category",              default: "common", null: false
+    t.boolean  "default",               default: false
+  end
+
+  add_index "services", ["category"], name: "index_services_on_category", using: :btree
   add_index "services", ["created_at", "id"], name: "index_services_on_created_at_and_id", using: :btree
+  add_index "services", ["default"], name: "index_services_on_default", using: :btree
   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: :cascade do |t|
-    t.string   "title",            limit: 255
+    t.string   "title"
     t.text     "content"
-    t.integer  "author_id",                                null: false
+    t.integer  "author_id",                    null: false
     t.integer  "project_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "file_name",        limit: 255
+    t.string   "file_name"
     t.datetime "expires_at"
-    t.string   "type",             limit: 255
-    t.integer  "visibility_level",             default: 0, null: false
+    t.string   "type"
+    t.integer  "visibility_level", default: 0, null: false
   end
 
   add_index "snippets", ["author_id"], name: "index_snippets_on_author_id", using: :btree
@@ -763,7 +774,7 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "subscriptions", force: :cascade do |t|
     t.integer  "user_id"
     t.integer  "subscribable_id"
-    t.string   "subscribable_type", limit: 255
+    t.string   "subscribable_type"
     t.boolean  "subscribed"
     t.datetime "created_at"
     t.datetime "updated_at"
@@ -774,10 +785,10 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   create_table "taggings", force: :cascade do |t|
     t.integer  "tag_id"
     t.integer  "taggable_id"
-    t.string   "taggable_type", limit: 255
+    t.string   "taggable_type"
     t.integer  "tagger_id"
-    t.string   "tagger_type",   limit: 255
-    t.string   "context",       limit: 255
+    t.string   "tagger_type"
+    t.string   "context"
     t.datetime "created_at"
   end
 
@@ -785,69 +796,70 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   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: :cascade do |t|
-    t.string  "name",           limit: 255
-    t.integer "taggings_count",             default: 0
+    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: :cascade do |t|
-    t.string   "email",                       limit: 255, default: "",    null: false
-    t.string   "encrypted_password",          limit: 255, default: "",    null: false
-    t.string   "reset_password_token",        limit: 255
+    t.string   "email",                       default: "",    null: false
+    t.string   "encrypted_password",          default: "",    null: false
+    t.string   "reset_password_token"
     t.datetime "reset_password_sent_at"
     t.datetime "remember_created_at"
-    t.integer  "sign_in_count",                           default: 0
+    t.integer  "sign_in_count",               default: 0
     t.datetime "current_sign_in_at"
     t.datetime "last_sign_in_at"
-    t.string   "current_sign_in_ip",          limit: 255
-    t.string   "last_sign_in_ip",             limit: 255
-    t.datetime "created_at"
-    t.datetime "updated_at"
-    t.string   "name",                        limit: 255
-    t.boolean  "admin",                                   default: false, null: false
-    t.integer  "projects_limit",                          default: 10
-    t.string   "skype",                       limit: 255, default: "",    null: false
-    t.string   "linkedin",                    limit: 255, default: "",    null: false
-    t.string   "twitter",                     limit: 255, default: "",    null: false
-    t.string   "authentication_token",        limit: 255
-    t.integer  "theme_id",                                default: 1,     null: false
-    t.string   "bio",                         limit: 255
-    t.integer  "failed_attempts",                         default: 0
+    t.string   "current_sign_in_ip"
+    t.string   "last_sign_in_ip"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+    t.string   "name"
+    t.boolean  "admin",                       default: false, null: false
+    t.integer  "projects_limit",              default: 10
+    t.string   "skype",                       default: "",    null: false
+    t.string   "linkedin",                    default: "",    null: false
+    t.string   "twitter",                     default: "",    null: false
+    t.string   "authentication_token"
+    t.integer  "theme_id",                    default: 1,     null: false
+    t.string   "bio"
+    t.integer  "failed_attempts",             default: 0
     t.datetime "locked_at"
-    t.string   "username",                    limit: 255
-    t.boolean  "can_create_group",                        default: true,  null: false
-    t.boolean  "can_create_team",                         default: true,  null: false
-    t.string   "state",                       limit: 255
-    t.integer  "color_scheme_id",                         default: 1,     null: false
-    t.integer  "notification_level",                      default: 1,     null: false
+    t.string   "username"
+    t.boolean  "can_create_group",            default: true,  null: false
+    t.boolean  "can_create_team",             default: true,  null: false
+    t.string   "state"
+    t.integer  "color_scheme_id",             default: 1,     null: false
+    t.integer  "notification_level",          default: 1,     null: false
     t.datetime "password_expires_at"
     t.integer  "created_by_id"
     t.datetime "last_credential_check_at"
-    t.string   "avatar",                      limit: 255
-    t.string   "confirmation_token",          limit: 255
+    t.string   "avatar"
+    t.string   "confirmation_token"
     t.datetime "confirmed_at"
     t.datetime "confirmation_sent_at"
-    t.string   "unconfirmed_email",           limit: 255
-    t.boolean  "hide_no_ssh_key",                         default: false
-    t.string   "website_url",                 limit: 255, default: "",    null: false
-    t.string   "notification_email",          limit: 255
-    t.boolean  "hide_no_password",                        default: false
-    t.boolean  "password_automatically_set",              default: false
-    t.string   "location",                    limit: 255
-    t.string   "encrypted_otp_secret",        limit: 255
-    t.string   "encrypted_otp_secret_iv",     limit: 255
-    t.string   "encrypted_otp_secret_salt",   limit: 255
-    t.boolean  "otp_required_for_login",                  default: false, null: false
+    t.string   "unconfirmed_email"
+    t.boolean  "hide_no_ssh_key",             default: false
+    t.string   "website_url",                 default: "",    null: false
+    t.string   "notification_email"
+    t.boolean  "hide_no_password",            default: false
+    t.boolean  "password_automatically_set",  default: false
+    t.string   "location"
+    t.string   "encrypted_otp_secret"
+    t.string   "encrypted_otp_secret_iv"
+    t.string   "encrypted_otp_secret_salt"
+    t.boolean  "otp_required_for_login",      default: false, null: false
     t.text     "otp_backup_codes"
-    t.string   "public_email",                limit: 255, default: "",    null: false
-    t.integer  "dashboard",                               default: 0
-    t.integer  "project_view",                            default: 0
+    t.string   "public_email",                default: "",    null: false
+    t.integer  "dashboard",                   default: 0
+    t.integer  "project_view",                default: 0
     t.integer  "consumed_timestep"
-    t.integer  "layout",                                  default: 0
-    t.boolean  "hide_project_limit",                      default: false
+    t.integer  "layout",                      default: 0
+    t.boolean  "hide_project_limit",          default: false
     t.string   "unlock_token"
     t.datetime "otp_grace_period_started_at"
+    t.boolean  "ldap_email",                  default: false, null: false
   end
 
   add_index "users", ["admin"], name: "index_users_on_admin", using: :btree
@@ -872,19 +884,19 @@ ActiveRecord::Schema.define(version: 20160106164438) do
   add_index "users_star_projects", ["user_id"], name: "index_users_star_projects_on_user_id", using: :btree
 
   create_table "web_hooks", force: :cascade do |t|
-    t.string   "url",                     limit: 255
+    t.string   "url",                     limit: 2000
     t.integer  "project_id"
     t.datetime "created_at"
     t.datetime "updated_at"
-    t.string   "type",                    limit: 255, default: "ProjectHook"
+    t.string   "type",                                 default: "ProjectHook"
     t.integer  "service_id"
-    t.boolean  "push_events",                         default: true,          null: false
-    t.boolean  "issues_events",                       default: false,         null: false
-    t.boolean  "merge_requests_events",               default: false,         null: false
-    t.boolean  "tag_push_events",                     default: false
-    t.boolean  "note_events",                         default: false,         null: false
-    t.boolean  "enable_ssl_verification",             default: true
-    t.boolean  "build_events",                        default: false,         null: false
+    t.boolean  "push_events",                          default: true,          null: false
+    t.boolean  "issues_events",                        default: false,         null: false
+    t.boolean  "merge_requests_events",                default: false,         null: false
+    t.boolean  "tag_push_events",                      default: false
+    t.boolean  "note_events",                          default: false,         null: false
+    t.boolean  "enable_ssl_verification",              default: true
+    t.boolean  "build_events",                         default: false,         null: false
   end
 
   add_index "web_hooks", ["created_at", "id"], name: "index_web_hooks_on_created_at_and_id", using: :btree
diff --git a/doc/README.md b/doc/README.md
index 5a05fbe50d3799b803cdfd82e9405fa961c0ac52..5089e1e70f6c2f9028e17eea110383294c44a5f8 100644
--- a/doc/README.md
+++ b/doc/README.md
@@ -30,6 +30,7 @@
 - [User permissions](ci/permissions/README.md)
 - [API](ci/api/README.md)
 - [Triggering builds through the API](ci/triggers/README.md)
+- [Build artifacts](ci/build_artifacts/README.md)
 
 ### CI Languages
 
@@ -53,6 +54,7 @@
 
 - [Custom git hooks](hooks/custom_hooks.md) Custom git hooks (on the filesystem) for when web hooks aren't enough.
 - [Install](install/README.md) Requirements, directory structures and installation from source.
+- [Restart GitLab](administration/restart_gitlab.md) Learn how to restart GitLab and its components
 - [Integration](integration/README.md) How to integrate with systems such as JIRA, Redmine, LDAP and Twitter.
 - [Issue closing](customization/issue_closing.md) Customize how to close an issue from commit messages.
 - [Libravatar](customization/libravatar.md) Use Libravatar for user avatars.
@@ -67,10 +69,13 @@
 - [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)
-- [Housekeeping](administration/housekeeping.md) Keep your git repository tidy and fast.
+- [Housekeeping](administration/housekeeping.md) Keep your Git repository tidy and fast.
+- [GitLab Performance Monitoring](monitoring/performance/introduction.md) Configure GitLab and InfluxDB for measuring performance metrics
 
 ## Contributor documentation
 
+- [Documentation styleguide](development/doc_styleguide.md) Use this styleguide if you are
+  contributing to documentation.
 - [Development](development/README.md) Explains the architecture and the guidelines for shell commands.
 - [Legal](legal/README.md) Contributor license agreements.
 - [Release](release/README.md) How to make the monthly and security releases.
diff --git a/doc/administration/environment_variables.md b/doc/administration/environment_variables.md
index 1eb3a74d304a6cbceaa4f207a8cbc2b76314d100..42a27dcf6d685f7cf0354849d7d56c581e4f3c8e 100644
--- a/doc/administration/environment_variables.md
+++ b/doc/administration/environment_variables.md
@@ -17,6 +17,8 @@ DATABASE_URL | url | For example: postgresql://localhost/blog_development?pool=5
 GITLAB_EMAIL_FROM | email | Email address used in the "From" field in mails sent by GitLab
 GITLAB_EMAIL_DISPLAY_NAME | string | Name used in the "From" field in mails sent by GitLab
 GITLAB_EMAIL_REPLY_TO | email | Email address used in the "Reply-To" field in mails sent by GitLab
+GITLAB_UNICORN_MEMORY_MIN | integer | The minimum memory threshold (in bytes) for the Unicorn worker killer
+GITLAB_UNICORN_MEMORY_MAX | integer | The maximum memory threshold (in bytes) for the Unicorn worker killer
 
 ## Complete database variables
 
diff --git a/doc/administration/restart_gitlab.md b/doc/administration/restart_gitlab.md
new file mode 100644
index 0000000000000000000000000000000000000000..483060395dd2fc996f90d54744f137a580a5cf03
--- /dev/null
+++ b/doc/administration/restart_gitlab.md
@@ -0,0 +1,145 @@
+# How to restart GitLab
+
+Depending on how you installed GitLab, there are different methods to restart
+its service(s).
+
+If you want the TL;DR versions, jump to:
+
+- [Omnibus GitLab restart](#omnibus-gitlab-restart)
+- [Omnibus GitLab reconfigure](#omnibus-gitlab-reconfigure)
+- [Source installation restart](#installations-from-source)
+
+## Omnibus installations
+
+If you have used the [Omnibus packages][omnibus-dl] to install GitLab, then
+you should already have `gitlab-ctl` in your `PATH`.
+
+`gitlab-ctl` interacts with the Omnibus packages and can be used to restart the
+GitLab Rails application (Unicorn) as well as the other components, like:
+
+- GitLab Workhorse
+- Sidekiq
+- PostgreSQL (if you are using the bundled one)
+- NGINX (if you are using the bundled one)
+- Redis (if you are using the bundled one)
+- [Mailroom][]
+- Logrotate
+
+### Omnibus GitLab restart
+
+There may be times in the documentation where you will be asked to _restart_
+GitLab. In that case, you need to run the following command:
+
+```bash
+sudo gitlab-ctl restart
+```
+
+The output should be similar to this:
+
+```
+ok: run: gitlab-workhorse: (pid 11291) 1s
+ok: run: logrotate: (pid 11299) 0s
+ok: run: mailroom: (pid 11306) 0s
+ok: run: nginx: (pid 11309) 0s
+ok: run: postgresql: (pid 11316) 1s
+ok: run: redis: (pid 11325) 0s
+ok: run: sidekiq: (pid 11331) 1s
+ok: run: unicorn: (pid 11338) 0s
+```
+
+To restart a component separately, you can append its service name to the
+`restart` command. For example, to restart **only** NGINX you would run:
+
+```bash
+sudo gitlab-ctl restart nginx
+```
+
+To check the status of GitLab services, run:
+
+```bash
+sudo gitlab-ctl status
+```
+
+Notice that all services say `ok: run`.
+
+Sometimes, components time out during the restart and sometimes they get stuck.
+In that case, you can use `gitlab-ctl kill <service>` to send the `SIGKILL`
+signal to the service, for example `sidekiq`. After that, a restart should
+perform fine.
+
+As a last resort, you can try to
+[reconfigure GitLab](#omnibus-gitlab-reconfigure) instead.
+
+### Omnibus GitLab reconfigure
+
+There may be times in the documentation where you will be asked to _reconfigure_
+GitLab. Remember that this method applies only for the Omnibus packages.
+
+Reconfigure Omnibus GitLab with:
+
+```bash
+sudo gitlab-ctl reconfigure
+```
+
+Reconfiguring GitLab should occur in the event that something in its
+configuration (`/etc/gitlab/gitlab.rb`) has changed.
+
+When you run this command, [Chef], the underlying configuration management
+application that powers Omnibus GitLab, will make sure that all directories,
+permissions, services, etc., are in place and in the same shape that they were
+initially shipped.
+
+It will also restart GitLab components where needed, if any of their
+configuration files have changed.
+
+If you manually edit any files in `/var/opt/gitlab` that are managed by Chef,
+running reconfigure will revert the changes AND restart the services that
+depend on those files.
+
+## Installations from source
+
+If you have followed the official installation guide to [install GitLab from
+source][install], run the following command to restart GitLab:
+
+```
+sudo service gitlab restart
+```
+
+The output should be similar to this:
+
+```
+Shutting down GitLab Unicorn
+Shutting down GitLab Sidekiq
+Shutting down GitLab Workhorse
+Shutting down GitLab MailRoom
+...
+GitLab is not running.
+Starting GitLab Unicorn
+Starting GitLab Sidekiq
+Starting GitLab Workhorse
+Starting GitLab MailRoom
+...
+The GitLab Unicorn web server with pid 28059 is running.
+The GitLab Sidekiq job dispatcher with pid 28176 is running.
+The GitLab Workhorse with pid 28122 is running.
+The GitLab MailRoom email processor with pid 28114 is running.
+GitLab and all its components are up and running.
+```
+
+This should restart Unicorn, Sidekiq, GitLab Workhorse and [Mailroom][]
+(if enabled). The init service file that does all the magic can be found on
+your server in `/etc/init.d/gitlab`.
+
+---
+
+If you are using other init systems, like systemd, you can check the
+[GitLab Recipes][gl-recipes] repository for some unofficial services. These are
+**not** officially supported so use them at your own risk.
+
+
+[omnibus-dl]: https://about.gitlab.com/downloads/ "Download the Omnibus packages"
+[install]: ../install/installation.md "Documentation to install GitLab from source"
+[mailroom]: ../incoming_email/README.md "Used for replying by email in GitLab issues and merge requests"
+[chef]: https://www.chef.io/chef/ "Chef official website"
+[src-service]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/lib/support/init.d/gitlab "GitLab init service file"
+[gl-recipes]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/init "GitLab Recipes repository"
diff --git a/doc/api/README.md b/doc/api/README.md
index 25a31b235cc77dc195bea89b70e02a7c106b34c9..4d2fb582833aaa01c1f3fc15105f0767cbe2cfaa 100644
--- a/doc/api/README.md
+++ b/doc/api/README.md
@@ -23,6 +23,9 @@
 - [Namespaces](namespaces.md)
 - [Settings](settings.md)
 - [Keys](keys.md)
+- [Builds](builds.md)
+- [Build triggers](build_triggers.md)
+- [Build Variables](build_variables.md)
 
 ## Clients
 
@@ -148,7 +151,53 @@ When listing resources you can pass the following parameters:
 - `page` (default: `1`) - page number
 - `per_page` (default: `20`, max: `100`) - number of items to list per page
 
-[Link headers](http://www.w3.org/wiki/LinkHeader) are send back with each response. These have `rel` prev/next/first/last and contain the relevant URL. Please use these instead of generating your own URLs.
+### Pagination Link header
+
+[Link headers](http://www.w3.org/wiki/LinkHeader) are sent back with each
+response. They have `rel` set to prev/next/first/last and contain the relevant
+URL. Please use these links instead of generating your own URLs.
+
+In the cURL example below, we limit the output to 3 items per page (`per_page=3`)
+and we request the second page (`page=2`) of [comments](notes.md) of the issue
+with ID `8` which belongs to the project with ID `8`:
+
+```bash
+curl -I -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/projects/8/issues/8/notes?per_page=3&page=2
+```
+
+The response will then be:
+
+```
+HTTP/1.1 200 OK
+Cache-Control: no-cache
+Content-Length: 1103
+Content-Type: application/json
+Date: Mon, 18 Jan 2016 09:43:18 GMT
+Link: <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="prev", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="next", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=1&per_page=3>; rel="first", <https://gitlab.example.com/api/v3/projects/8/issues/8/notes?page=3&per_page=3>; rel="last"
+Status: 200 OK
+Vary: Origin
+X-Next-Page: 3
+X-Page: 2
+X-Per-Page: 3
+X-Prev-Page: 1
+X-Request-Id: 732ad4ee-9870-4866-a199-a9db0cde3c86
+X-Runtime: 0.108688
+X-Total: 8
+X-Total-Pages: 3
+```
+
+### Other pagination headers
+
+Additional pagination headers are also sent back.
+
+| Header | Description |
+| ------ | ----------- |
+| `X-Total`       | The total number of items |
+| `X-Total-Pages` | The total number of pages |
+| `X-Per-Page`    | The number of items per page |
+| `X-Page`        | The index of the current page (starting at 1) |
+| `X-Next-Page`   | The index of the next page |
+| `X-Prev-Page`   | The index of the previous page |
 
 ## id vs iid
 
diff --git a/doc/api/build_triggers.md b/doc/api/build_triggers.md
new file mode 100644
index 0000000000000000000000000000000000000000..4a12e962b620de174c84fc89b2862ec77299fd38
--- /dev/null
+++ b/doc/api/build_triggers.md
@@ -0,0 +1,108 @@
+# Build triggers
+
+You can read more about [triggering builds through the API](../ci/triggers/README.md).
+
+## List project triggers
+
+Get a list of project's build triggers.
+
+```
+GET /projects/:id/triggers
+```
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| `id`      | integer | yes      | The ID of a project |
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers"
+```
+
+```json
+[
+    {
+        "created_at": "2015-12-23T16:24:34.716Z",
+        "deleted_at": null,
+        "last_used": "2016-01-04T15:41:21.986Z",
+        "token": "fbdb730c2fbdb095a0862dbd8ab88b",
+        "updated_at": "2015-12-23T16:24:34.716Z"
+    },
+    {
+        "created_at": "2015-12-23T16:25:56.760Z",
+        "deleted_at": null,
+        "last_used": null,
+        "token": "7b9148c158980bbd9bcea92c17522d",
+        "updated_at": "2015-12-23T16:25:56.760Z"
+    }
+]
+```
+
+## Get trigger details
+
+Get details of project's build trigger.
+
+```
+GET /projects/:id/triggers/:token
+```
+
+| Attribute | Type    | required | Description              |
+|-----------|---------|----------|--------------------------|
+| `id`      | integer | yes      | The ID of a project      |
+| `token`   | string  | yes      | The `token` of a trigger |
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
+```
+
+```json
+{
+    "created_at": "2015-12-23T16:25:56.760Z",
+    "deleted_at": null,
+    "last_used": null,
+    "token": "7b9148c158980bbd9bcea92c17522d",
+    "updated_at": "2015-12-23T16:25:56.760Z"
+}
+```
+
+## Create a project trigger
+
+Create a build trigger for a project.
+
+```
+POST /projects/:id/triggers
+```
+
+| Attribute | Type    | required | Description              |
+|-----------|---------|----------|--------------------------|
+| `id`      | integer | yes      | The ID of a project      |
+
+```
+curl -X POST -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers"
+```
+
+```json
+{
+    "created_at": "2016-01-07T09:53:58.235Z",
+    "deleted_at": null,
+    "last_used": null,
+    "token": "6d056f63e50fe6f8c5f8f4aa10edb7",
+    "updated_at": "2016-01-07T09:53:58.235Z"
+}
+```
+
+## Remove a project trigger
+
+Remove a project's build trigger.
+
+```
+DELETE /projects/:id/triggers/:token
+```
+
+| Attribute | Type    | required | Description              |
+|-----------|---------|----------|--------------------------|
+| `id`      | integer | yes      | The ID of a project      |
+| `token`   | string  | yes      | The `token` of a project |
+
+```
+curl -X DELETE -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/triggers/7b9148c158980bbd9bcea92c17522d"
+```
diff --git a/doc/api/build_variables.md b/doc/api/build_variables.md
new file mode 100644
index 0000000000000000000000000000000000000000..b96f1bdac8ab0971a5b21573c00555738b6cd85c
--- /dev/null
+++ b/doc/api/build_variables.md
@@ -0,0 +1,128 @@
+# Build Variables
+
+## List project variables
+
+Get list of a project's build variables.
+
+```
+GET /projects/:id/variables
+```
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| `id`      | integer | yes      | The ID of a project |
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables"
+```
+
+```json
+[
+    {
+        "key": "TEST_VARIABLE_1",
+        "value": "TEST_1"
+    },
+    {
+        "key": "TEST_VARIABLE_2",
+        "value": "TEST_2"
+    }
+]
+```
+
+## Show variable details
+
+Get the details of a project's specific build variable.
+
+```
+GET /projects/:id/variables/:key
+```
+
+| Attribute | Type    | required | Description           |
+|-----------|---------|----------|-----------------------|
+| `id`      | integer | yes      | The ID of a project   |
+| `key`     | string  | yes      | The `key` of a variable |
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/TEST_VARIABLE_1"
+```
+
+```json
+{
+    "key": "TEST_VARIABLE_1",
+    "value": "TEST_1"
+}
+```
+
+## Create variable
+
+Create a new build variable.
+
+```
+POST /projects/:id/variables
+```
+
+| Attribute | Type    | required | Description           |
+|-----------|---------|----------|-----------------------|
+| `id`      | integer | yes      | The ID of a project   |
+| `key`     | string  | yes      | The `key` of a variable; must have no more than 255 characters; only `A-Z`, `a-z`, `0-9`, and `_` are allowed |
+| `value`   | string  | yes      | The `value` of a variable |
+
+```
+curl -X POST -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables" -F "key=NEW_VARIABLE" -F "value=new value"
+```
+
+```json
+{
+    "key": "NEW_VARIABLE",
+    "value": "new value"
+}
+```
+
+## Update variable
+
+Update a project's build variable.
+
+```
+PUT /projects/:id/variables/:key
+```
+
+| Attribute | Type    | required | Description             |
+|-----------|---------|----------|-------------------------|
+| `id`      | integer | yes      | The ID of a project     |
+| `key`     | string  | yes      | The `key` of a variable   |
+| `value`   | string  | yes      | The `value` of a variable |
+
+```
+curl -X PUT -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/NEW_VARIABLE" -F "value=updated value"
+```
+
+```json
+{
+    "key": "NEW_VARIABLE",
+    "value": "updated value"
+}
+```
+
+## Remove variable
+
+Remove a project's build variable.
+
+```
+DELETE /projects/:id/variables/:key
+```
+
+| Attribute | Type    | required | Description             |
+|-----------|---------|----------|-------------------------|
+| `id`      | integer | yes      | The ID of a project     |
+| `key`     | string  | yes      | The `key` of a variable |
+
+```
+curl -X DELETE -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/variables/VARIABLE_1"
+```
+
+```json
+{
+    "key": "VARIABLE_1",
+    "value": "VALUE_1"
+}
+```
diff --git a/doc/api/builds.md b/doc/api/builds.md
new file mode 100644
index 0000000000000000000000000000000000000000..ecb50754c88c25f84af919e7a9f62c179cd18780
--- /dev/null
+++ b/doc/api/builds.md
@@ -0,0 +1,360 @@
+# Builds API
+
+## List project builds
+
+Get a list of builds in a project.
+
+```
+GET /projects/:id/builds
+```
+
+### Parameters
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| id        | integer | yes      | The ID of a project |
+| scope     | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
+
+### Example of request
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds"
+```
+
+### Example of response
+
+```json
+[
+    {
+        "commit": {
+            "author_email": "admin@example.com",
+            "author_name": "Administrator",
+            "created_at": "2015-12-24T16:51:14.000+01:00",
+            "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+            "message": "Test the CI integration.",
+            "short_id": "0ff3ae19",
+            "title": "Test the CI integration."
+        },
+        "coverage": null,
+        "created_at": "2015-12-24T15:51:21.802Z",
+        "download_url": null,
+        "finished_at": "2015-12-24T17:54:27.895Z",
+        "id": 7,
+        "name": "teaspoon",
+        "ref": "master",
+        "runner": null,
+        "stage": "test",
+        "started_at": "2015-12-24T17:54:27.722Z",
+        "status": "failed",
+        "tag": false,
+        "user": {
+            "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+            "bio": null,
+            "created_at": "2015-12-21T13:14:24.077Z",
+            "id": 1,
+            "is_admin": true,
+            "linkedin": "",
+            "name": "Administrator",
+            "skype": "",
+            "state": "active",
+            "twitter": "",
+            "username": "root",
+            "web_url": "http://gitlab.dev/u/root",
+            "website_url": ""
+        }
+    },
+    {
+        "commit": {
+            "author_email": "admin@example.com",
+            "author_name": "Administrator",
+            "created_at": "2015-12-24T16:51:14.000+01:00",
+            "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+            "message": "Test the CI integration.",
+            "short_id": "0ff3ae19",
+            "title": "Test the CI integration."
+        },
+        "coverage": null,
+        "created_at": "2015-12-24T15:51:21.727Z",
+        "download_url": null,
+        "finished_at": "2015-12-24T17:54:24.921Z",
+        "id": 6,
+        "name": "spinach:other",
+        "ref": "master",
+        "runner": null,
+        "stage": "test",
+        "started_at": "2015-12-24T17:54:24.729Z",
+        "status": "failed",
+        "tag": false,
+        "user": {
+            "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+            "bio": null,
+            "created_at": "2015-12-21T13:14:24.077Z",
+            "id": 1,
+            "is_admin": true,
+            "linkedin": "",
+            "name": "Administrator",
+            "skype": "",
+            "state": "active",
+            "twitter": "",
+            "username": "root",
+            "web_url": "http://gitlab.dev/u/root",
+            "website_url": ""
+        }
+    }
+]
+```
+
+## List commit builds
+
+Get a list of builds for specific commit in a project.
+
+```
+GET /projects/:id/repository/commits/:sha/builds
+```
+
+### Parameters
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| id        | integer | yes      | The ID of a project |
+| sha       | string  | yes      | The SHA id of a commit |
+| scope     | string|array of strings | no | The scope of builds to show, one or array of: `pending`, `running`, `failed`, `success`, `canceled`; showing all builds if none provided |
+
+### Example of request
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/repository/commits/0ff3ae198f8601a285adcf5c0fff204ee6fba5fd/builds"
+```
+
+### Example of response
+
+```json
+[
+    {
+        "commit": {
+            "author_email": "admin@example.com",
+            "author_name": "Administrator",
+            "created_at": "2015-12-24T16:51:14.000+01:00",
+            "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+            "message": "Test the CI integration.",
+            "short_id": "0ff3ae19",
+            "title": "Test the CI integration."
+        },
+        "coverage": null,
+        "created_at": "2016-01-11T10:13:33.506Z",
+        "download_url": null,
+        "finished_at": "2016-01-11T10:14:09.526Z",
+        "id": 69,
+        "name": "rubocop",
+        "ref": "master",
+        "runner": null,
+        "stage": "test",
+        "started_at": null,
+        "status": "canceled",
+        "tag": false,
+        "user": null
+    },
+    {
+        "commit": {
+            "author_email": "admin@example.com",
+            "author_name": "Administrator",
+            "created_at": "2015-12-24T16:51:14.000+01:00",
+            "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+            "message": "Test the CI integration.",
+            "short_id": "0ff3ae19",
+            "title": "Test the CI integration."
+        },
+        "coverage": null,
+        "created_at": "2015-12-24T15:51:21.957Z",
+        "download_url": null,
+        "finished_at": "2015-12-24T17:54:33.913Z",
+        "id": 9,
+        "name": "brakeman",
+        "ref": "master",
+        "runner": null,
+        "stage": "test",
+        "started_at": "2015-12-24T17:54:33.727Z",
+        "status": "failed",
+        "tag": false,
+        "user": {
+            "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+            "bio": null,
+            "created_at": "2015-12-21T13:14:24.077Z",
+            "id": 1,
+            "is_admin": true,
+            "linkedin": "",
+            "name": "Administrator",
+            "skype": "",
+            "state": "active",
+            "twitter": "",
+            "username": "root",
+            "web_url": "http://gitlab.dev/u/root",
+            "website_url": ""
+        }
+    }
+]
+```
+
+## Get a single build
+
+Get a single build of a project
+
+```
+GET /projects/:id/builds/:build_id
+```
+
+### Parameters
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| id        | integer | yes      | The ID of a project |
+| build\_id  | integer | yes      | The ID of a build   |
+
+### Example of request
+
+```
+curl -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/8"
+```
+
+### Example of response
+
+```json
+{
+    "commit": {
+        "author_email": "admin@example.com",
+        "author_name": "Administrator",
+        "created_at": "2015-12-24T16:51:14.000+01:00",
+        "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+        "message": "Test the CI integration.",
+        "short_id": "0ff3ae19",
+        "title": "Test the CI integration."
+    },
+    "coverage": null,
+    "created_at": "2015-12-24T15:51:21.880Z",
+    "download_url": null,
+    "finished_at": "2015-12-24T17:54:31.198Z",
+    "id": 8,
+    "name": "rubocop",
+    "ref": "master",
+    "runner": null,
+    "stage": "test",
+    "started_at": "2015-12-24T17:54:30.733Z",
+    "status": "failed",
+    "tag": false,
+    "user": {
+        "avatar_url": "http://www.gravatar.com/avatar/e64c7d89f26bd1972efa854d13d7dd61?s=80&d=identicon",
+        "bio": null,
+        "created_at": "2015-12-21T13:14:24.077Z",
+        "id": 1,
+        "is_admin": true,
+        "linkedin": "",
+        "name": "Administrator",
+        "skype": "",
+        "state": "active",
+        "twitter": "",
+        "username": "root",
+        "web_url": "http://gitlab.dev/u/root",
+        "website_url": ""
+    }
+}
+```
+
+## Cancel a build
+
+Cancel a single build of a project
+
+```
+POST /projects/:id/builds/:build_id/cancel
+```
+
+### Parameters
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| id        | integer | yes      | The ID of a project |
+| build\_id | integer | yes      | The ID of a build   |
+
+### Example of request
+
+```
+curl -X POST -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/cancel"
+```
+
+### Example of response
+
+```json
+{
+    "commit": {
+        "author_email": "admin@example.com",
+        "author_name": "Administrator",
+        "created_at": "2015-12-24T16:51:14.000+01:00",
+        "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+        "message": "Test the CI integration.",
+        "short_id": "0ff3ae19",
+        "title": "Test the CI integration."
+    },
+    "coverage": null,
+    "created_at": "2016-01-11T10:13:33.506Z",
+    "download_url": null,
+    "finished_at": "2016-01-11T10:14:09.526Z",
+    "id": 69,
+    "name": "rubocop",
+    "ref": "master",
+    "runner": null,
+    "stage": "test",
+    "started_at": null,
+    "status": "canceled",
+    "tag": false,
+    "user": null
+}
+```
+
+## Retry a build
+
+Retry a single build of a project
+
+```
+POST /projects/:id/builds/:build_id/retry
+```
+
+### Parameters
+
+| Attribute | Type    | required | Description         |
+|-----------|---------|----------|---------------------|
+| id        | integer | yes      | The ID of a project |
+| build\_id | integer | yes      | The ID of a build   |
+
+### Example of request
+
+```
+curl -X POST -H "PRIVATE_TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/1/builds/1/retry"
+```
+
+### Example of response
+
+```json
+{
+    "commit": {
+        "author_email": "admin@example.com",
+        "author_name": "Administrator",
+        "created_at": "2015-12-24T16:51:14.000+01:00",
+        "id": "0ff3ae198f8601a285adcf5c0fff204ee6fba5fd",
+        "message": "Test the CI integration.",
+        "short_id": "0ff3ae19",
+        "title": "Test the CI integration."
+    },
+    "coverage": null,
+    "created_at": "2016-01-11T10:13:33.506Z",
+    "download_url": null,
+    "finished_at": null,
+    "id": 69,
+    "name": "rubocop",
+    "ref": "master",
+    "runner": null,
+    "stage": "test",
+    "started_at": null,
+    "status": "pending",
+    "tag": false,
+    "user": null
+}
+```
diff --git a/doc/api/groups.md b/doc/api/groups.md
index 808675d8605006c142a25eb188d66190c6bb73da..d47e79ba47f20aa52d2f3e987ea3f5e24245df5a 100644
--- a/doc/api/groups.md
+++ b/doc/api/groups.md
@@ -33,6 +33,7 @@ GET /groups/:id/projects
 Parameters:
 
 - `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
 - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
 - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
 - `search` (optional) - Return list of authorized projects according to a search criteria
diff --git a/doc/api/projects.md b/doc/api/projects.md
index 37d74216c1b5a7850fd1ae7d05017e4fb2ab2d23..3f372c955d22ed07dca9fa22c5e9e2fe66d5b73a 100644
--- a/doc/api/projects.md
+++ b/doc/api/projects.md
@@ -29,6 +29,7 @@ GET /projects
 Parameters:
 
 - `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
 - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
 - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
 - `search` (optional) - Return list of authorized projects according to a search criteria
@@ -76,7 +77,10 @@ Parameters:
       "updated_at": "2013-09-30T13: 46: 02Z"
     },
     "archived": false,
-    "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png"
+    "avatar_url": "http://example.com/uploads/project/avatar/4/uploads/avatar.png",
+    "shared_runners_enabled": true,
+    "forks_count": 0,
+    "star_count": 0
   },
   {
     "id": 6,
@@ -129,7 +133,11 @@ Parameters:
       }
     },
     "archived": false,
-    "avatar_url": null
+    "avatar_url": null,
+    "shared_runners_enabled": true,
+    "forks_count": 0,
+    "star_count": 0,
+    "runners_token": "b8547b1dc37721d05889db52fa2f02"
   }
 ]
 ```
@@ -145,6 +153,7 @@ GET /projects/owned
 Parameters:
 
 - `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
 - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
 - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
 - `search` (optional) - Return list of authorized projects according to a search criteria
@@ -160,6 +169,7 @@ GET /projects/starred
 Parameters:
 
 - `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
 - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
 - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
 - `search` (optional) - Return list of authorized projects according to a search criteria
@@ -175,6 +185,7 @@ GET /projects/all
 Parameters:
 
 - `archived` (optional) - if passed, limit by archived status
+- `visibility` (optional) - if passed, limit by visibility `public`, `internal`, `private`
 - `order_by` (optional) - Return requests ordered by `id`, `name`, `path`, `created_at`, `updated_at` or `last_activity_at` fields. Default is `created_at`
 - `sort` (optional) - Return requests sorted in `asc` or `desc` order. Default is `desc`
 - `search` (optional) - Return list of authorized projects according to a search criteria
@@ -244,7 +255,11 @@ Parameters:
     }
   },
   "archived": false,
-  "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png"
+  "avatar_url": "http://example.com/uploads/project/avatar/3/uploads/avatar.png",
+  "shared_runners_enabled": true,
+  "forks_count": 0,
+  "star_count": 0,
+  "runners_token": "b8bc4a7a29eb76ea83cf79e4908c2b"
 }
 ```
 
diff --git a/doc/api/users.md b/doc/api/users.md
index 773fe36d277b28a9de7167c186bc4c857e37eba7..b7fc903825ea2faedcc5aa77e47b41cacceb8b53 100644
--- a/doc/api/users.md
+++ b/doc/api/users.md
@@ -558,7 +558,8 @@ Parameters:
 
 - `uid` (required) - id of specified user
 
-Will return `200 OK` on success, or `404 User Not Found` is user cannot be found.
+Will return `200 OK` on success, `404 User Not Found` is user cannot be found or 
+`403 Forbidden` when trying to block an already blocked user by LDAP synchronization.
 
 ## Unblock user
 
@@ -572,4 +573,5 @@ Parameters:
 
 - `uid` (required) - id of specified user
 
-Will return `200 OK` on success, or `404 User Not Found` is user cannot be found.
+Will return `200 OK` on success, `404 User Not Found` is user cannot be found or
+`403 Forbidden` when trying to unblock a user blocked by LDAP synchronization.
diff --git a/doc/ci/README.md b/doc/ci/README.md
index 4cdd2e1ad3394017887160e365557e2f7028a528..5886829be51084594c0637561b464f89d2866e3c 100644
--- a/doc/ci/README.md
+++ b/doc/ci/README.md
@@ -12,6 +12,7 @@
 * [Using Variables](variables/README.md)
 * [Using SSH keys](ssh_keys/README.md)
 * [Triggering builds through the API](triggers/README.md)
+* [Build artifacts](build_artifacts/README.md)
 
 ### Languages
 
diff --git a/doc/ci/api/builds.md b/doc/ci/api/builds.md
index 3b5008ccdb47eab1d94ceef85f574d304fb71728..018ca22dbbd749fe3d2f386fee98f45df7722d90 100644
--- a/doc/ci/api/builds.md
+++ b/doc/ci/api/builds.md
@@ -18,18 +18,62 @@ Returns:
 
 ```json
 {
-  "id" : 79,
-  "commands" : "",
-  "path" : "",
-  "ref" : "",
-  "sha" : "",
-  "project_id" : 6,
-  "repo_url" : "git@demo.gitlab.com:gitlab/gitlab-shell.git",
-  "before_sha" : ""
+  "id": 48584,
+  "ref": "0.1.1",
+  "tag": true,
+  "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
+  "status": "success",
+  "name": "pages",
+  "token": "9dd60b4f1a439d1765357446c1084c",
+  "stage": "test",
+  "project_id": 479,
+  "project_name": "test",
+  "commands": "echo commands",
+  "repo_url": "http://gitlab-ci-token:token@gitlab.example/group/test.git",
+  "before_sha": "0000000000000000000000000000000000000000",
+  "allow_git_fetch": false,
+  "options": {
+    "image": "docker:image",
+    "artifacts": {
+      "paths": [
+        "public"
+      ]
+    },
+    "cache": {
+      "paths": [
+        "vendor"
+      ]
+    }
+  },
+  "timeout": 3600,
+  "variables": [
+    {
+      "key": "CI_BUILD_TAG",
+      "value": "0.1.1",
+      "public": true
+    }
+  ],
+  "depends_on_builds": [
+    {
+      "id": 48584,
+      "ref": "0.1.1",
+      "tag": true,
+      "sha": "d63117656af6ff57d99e50cc270f854691f335ad",
+      "status": "success",
+      "name": "build",
+      "token": "9dd60b4f1a439d1765357446c1084c",
+      "stage": "build",
+      "project_id": 479,
+      "project_name": "test",
+      "artifacts_file": {
+        "filename": "artifacts.zip",
+        "size": 0
+      }
+    }
+  ]
 }
 ```
 
-
 ### Update details of an existing build
 
     PUT /ci/builds/:id
diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..b02caa9edff006e72bb85cc8591a02df01cbeea5
--- /dev/null
+++ b/doc/ci/build_artifacts/README.md
@@ -0,0 +1,173 @@
+# Introduction to build artifacts
+
+Artifacts is a list of files and directories which are attached to a build
+after it completes successfully.
+
+Since GitLab 8.2 and [GitLab Runner] 0.7.0, build artifacts that are created by
+GitLab Runner are uploaded to GitLab and are downloadable as a single archive
+(`tar.gz`) using the GitLab UI.
+
+Starting from GitLab 8.4 and GitLab Runner 1.0, the artifacts archive format
+changed to `ZIP`, and it is now possible to browse its contents, with the added
+ability of downloading the files separately.
+
+## Enabling build artifacts
+
+If you are searching for ways to use the artifacts feature, jump to
+[Defining artifacts in `.gitlab-ci.yml`](#defining-artifacts-in-gitlab-ciyml).
+
+The artifacts feature is enabled by default in all GitLab installations.
+
+If by any chance you want to disable the artifacts feature on your GitLab
+instance, follow the steps below.
+
+---
+
+**In Omnibus installations:**
+
+1. Edit `/etc/gitlab/gitlab.rb` and add the following line:
+
+    ```ruby
+    gitlab_rails['artifacts_enabled'] = false
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**In installations from source:**
+
+1. Edit `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
+
+    ```yaml
+    artifacts:
+      enabled: false
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+
+## Defining artifacts in `.gitlab-ci.yml`
+
+A simple example of using the artifacts definition in `.gitlab-ci.yml` would be
+the following:
+
+```yaml
+pdf:
+  script: xelatex mycv.tex
+  artifacts:
+    paths:
+    - mycv.pdf
+```
+
+A job named `pdf` calls the `xelatex` command in order to build a pdf file from
+the latex source file `mycv.tex`. We then define the `artifacts` paths which in
+turn are defined with the `paths` keyword. All paths to files and directories
+are relative to the repository that was cloned during the build.
+
+For more examples on artifacts, follow the
+[separate artifacts yaml documentation](../yaml/README.md#artifacts).
+
+## Storing build artifacts
+
+After a successful build, GitLab Runner uploads an archive containing the build
+artifacts to GitLab.
+
+To change the location where the artifacts are stored, follow the steps below.
+
+---
+
+**In Omnibus installations:**
+
+_The artifacts are stored by default in
+`/var/opt/gitlab/gitlab-rails/shared/artifacts`._
+
+1. To change the storage path for example to `/mnt/storage/artifacts`, edit
+   `/etc/gitlab/gitlab.rb` and add the following line:
+
+    ```ruby
+    gitlab_rails['artifacts_path'] = "/mnt/storage/artifacts"
+    ```
+
+1. Save the file and [reconfigure GitLab][] for the changes to take effect.
+
+---
+
+**In installations from source:**
+
+_The artifacts are stored by default in
+`/home/git/gitlab/shared/artifacts`._
+
+1. To change the storage path for example to `/mnt/storage/artifacts`, edit
+   `/home/git/gitlab/config/gitlab.yml` and add or amend the following lines:
+
+    ```yaml
+    artifacts:
+      enabled: true
+      path: /mnt/storage/artifacts
+    ```
+
+1. Save the file and [restart GitLab][] for the changes to take effect.
+
+## Browsing build artifacts
+
+When GitLab receives an artifacts archive, an archive metadata file is also
+generated. This metadata file describes all the entries that are located in the
+artifacts archive itself. The metadata file is in a binary format, with
+additional GZIP compression.
+
+GitLab does not extract the artifacts archive in order to save space, memory
+and disk I/O. It instead inspects the metadata file which contains all the
+relevant information. This is especially important when there is a lot of
+artifacts, or an archive is a very large file.
+
+---
+
+After a successful build, if you visit the build's specific page, you can see
+that there are two buttons.
+
+One is for downloading the artifacts archive and the other for browsing its
+contents.
+
+![Build artifacts browser button](img/build_artifacts_browser_button.png)
+
+---
+
+The archive browser shows the name and the actual file size of each file in the
+archive. If your artifacts contained directories, then you are also able to
+browse inside them.
+
+Below you can see an image of three different file formats, as well as two
+directories.
+
+![Build artifacts browser](img/build_artifacts_browser.png)
+
+---
+
+## Downloading build artifacts
+
+If you need to download the whole archive, there are buttons in various places
+inside GitLab that make that possible.
+
+1. While on the builds page, you can see the download icon for each build's
+   artifacts archive in the right corner
+
+1. While inside a specific build, you are presented with a download button
+   along with the one that browses the archive
+
+1. And finally, when browsing and archive you can see the download button at
+   the top right corner
+
+---
+
+Note that GitLab does not extract the entire artifacts archive to send just a
+single file to the user.
+
+When clicking on a specific file, [GitLab Workhorse] extracts it from the
+archive and the download begins.
+
+This implementation saves space, memory and disk I/O.
+
+[gitlab runner]: https://gitlab.com/gitlab-org/gitlab-ci-multi-runner "GitLab Runner repository"
+[reconfigure gitlab]: ../../administration/restart_gitlab.md "How to restart GitLab documentation"
+[restart gitlab]: ../../administration/restart_gitlab.md "How to restart GitLab documentation"
+[gitlab workhorse]: https://gitlab.com/gitlab-org/gitlab-workhorse "GitLab Workhorse repository"
diff --git a/doc/ci/build_artifacts/img/build_artifacts_browser.png b/doc/ci/build_artifacts/img/build_artifacts_browser.png
new file mode 100644
index 0000000000000000000000000000000000000000..73ed4eeb92781c798c3461283ca84d34d462c61c
Binary files /dev/null and b/doc/ci/build_artifacts/img/build_artifacts_browser.png differ
diff --git a/doc/ci/build_artifacts/img/build_artifacts_browser_button.png b/doc/ci/build_artifacts/img/build_artifacts_browser_button.png
new file mode 100644
index 0000000000000000000000000000000000000000..f5d15bc3e7d1bf1e5db53efb2e4ef7e771576a33
Binary files /dev/null and b/doc/ci/build_artifacts/img/build_artifacts_browser_button.png differ
diff --git a/doc/ci/docker/using_docker_images.md b/doc/ci/docker/using_docker_images.md
index 31458d61674d1c45e82907443d65638abfe23f42..63fe840b369b1a9d56c6f335260e3115b498522d 100644
--- a/doc/ci/docker/using_docker_images.md
+++ b/doc/ci/docker/using_docker_images.md
@@ -174,7 +174,7 @@ The alias hostname for the service is made from the image name following these
 rules:
 
 1. Everything after `:` is stripped
-2. Backslash (`/`) is replaced with double underscores (`__`)
+2. Slash (`/`) is replaced with double underscores (`__`)
 
 ## Configuring services
 
diff --git a/doc/ci/variables/README.md b/doc/ci/variables/README.md
index b99ea25a3fe33ef74ceef3a9a4202452e897e95e..862cacda586bb21fb56a49cd5c93261295799921 100644
--- a/doc/ci/variables/README.md
+++ b/doc/ci/variables/README.md
@@ -56,7 +56,7 @@ export CI_SERVER_VERSION=""
 ```
 
 ### YAML-defined variables
-**This feature requires GitLab Runner 0.5.0 or higher**
+**This feature requires GitLab Runner 0.5.0 or higher and GitLab CI 7.14 or higher **
 
 GitLab CI allows you to add to `.gitlab-ci.yml` variables that are set in build environment.
 The variables are stored in repository and are meant to store non-sensitive project configuration, ie. RAILS_ENV or DATABASE_URL.
diff --git a/doc/ci/yaml/README.md b/doc/ci/yaml/README.md
index fd0d49de4e438ba654ee160276733cb38768069d..3b594df659d67d27ba1a5a3438521dc84bb4e188 100644
--- a/doc/ci/yaml/README.md
+++ b/doc/ci/yaml/README.md
@@ -135,10 +135,9 @@ thus allowing to fine tune them.
 ### cache
 
 `cache` is used to specify a list of files and directories which should be
-cached between builds. Caches are stored according to the branch/ref and the
-job name. They are not currently shared between different job names or between
-branches/refs, which means that caching will benefit you if you push subsequent
-commits to an existing feature branch.
+cached between builds.
+
+**By default the caching is enabled per-job and per-branch.**
 
 If `cache` is defined outside the scope of the jobs, it means it is set
 globally and all jobs will use its definition.
@@ -152,6 +151,59 @@ cache:
   - binaries/
 ```
 
+#### cache:key
+
+_**Note:** Introduced in GitLab Runner v1.0.0._
+
+The `key` directive allows you to define the affinity of caching
+between jobs, allowing to have a single cache for all jobs,
+cache per-job, cache per-branch or any other way you deem proper.
+
+This allows you to fine tune caching, allowing you to cache data between different jobs or even different branches.
+The `cache:key` variable can use any of the [predefined variables](../variables/README.md):
+
+Example configurations:
+
+To enable per-job caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_NAME"
+      untracked: true
+    ```
+
+To enable per-branch caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+To enable per-job and per-branch caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_NAME/$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+To enable per-branch and per-stage caching:
+
+    ```yaml
+    cache:
+      key: "$CI_BUILD_STAGE/$CI_BUILD_REF_NAME"
+      untracked: true
+    ```
+
+If you use **Windows Batch** to run your shell scripts you need to replace the `$` with `%`:
+
+    ```yaml
+    cache:
+      key: "%CI_BUILD_STAGE%/%CI_BUILD_REF_NAME%"
+      untracked: true
+    ```
+
 ## Jobs
 
 `.gitlab-ci.yml` allows you to specify an unlimited number of jobs. Each job
diff --git a/doc/development/doc_styleguide.md b/doc/development/doc_styleguide.md
new file mode 100644
index 0000000000000000000000000000000000000000..caaa4032db26ff7fd1e562fb886655c5f6f650b5
--- /dev/null
+++ b/doc/development/doc_styleguide.md
@@ -0,0 +1,249 @@
+# Documentation styleguide
+
+This styleguide recommends best practices to improve documentation and to keep
+it organized and easy to find.
+
+## Naming
+
+- When creating a new document and it has more than one word in its name,
+  make sure to use underscores instead of spaces or dashes (`-`). For example,
+  a proper naming would be `import_projects_from_github.md`. The same rule
+  applies to images.
+
+## Text
+
+- Split up long lines, this makes it much easier to review and edit. Only
+  double line breaks are shown as a full line break in [GitLab markdown][gfm].
+  80-100 characters is a good line length
+- Make sure that the documentation is added in the correct directory and that
+  there's a link to it somewhere useful
+- Do not duplicate information
+- Be brief and clear
+- Unless there's a logical reason not to, add documents in alphabetical order
+- Write in US English
+- Use [single spaces][] instead of double spaces
+
+## Formatting
+
+- Use dashes (`-`) for unordered lists instead of asterisks (`*`)
+- Use the number one (`1`) for ordered lists
+- Use underscores (`_`) to mark a word or text in italics
+- Use double asterisks (`**`) to mark a word or text in bold
+- When using lists, prefer not to end each item with a period. You can use
+  them if there are multiple sentences, just keep the last sentence without
+  a period
+
+## Headings
+
+- Add only one H1 title in each document, by adding `#` at the beginning of
+  it (when using markdown). For subheadings, use `##`, `###` and so on
+- Avoid putting numbers in headings. Numbers shift, hence documentation anchor
+  links shift too, which eventually leads to dead links. If you think it is
+  compelling to add numbers in headings, make sure to at least discuss it with
+  someone in the Merge Request
+- When introducing a new document, be careful for the headings to be
+  grammatically and syntactically correct. It is advised to mention one or all
+  of the following GitLab members for a review: `@axil`, `@rspeicher`,
+  `@dblessing`, `@ashleys`, `@nearlythere`. This is to ensure that no document
+  with wrong heading is going live without an audit, thus preventing dead links
+  and redirection issues when corrected
+- Leave exactly one newline after a heading
+
+## Links
+
+- If a link makes the paragraph to span across multiple lines, do not use
+  the regular Markdown approach: `[Text](https://example.com)`. Instead use
+  `[Text][identifier]` and at the very bottom of the document add:
+  `[identifier]: https://example.com`. This is another way to create Markdown
+  links which keeps the document clear and concise. Bonus points if you also
+  add an alternative text: `[identifier]: https://example.com "Alternative text"`
+  that appears when hovering your mouse on a link
+
+## Images
+
+- Place images in a separate directory named `img/` in the same directory where
+  the `.md` document that you're working on is located. Always prepend their
+  names with the name of the document that they will be included in. For
+  example, if there is a document called `twitter.md`, then a valid image name
+  could be `twitter_login_screen.png`.
+- Images should have a specific, non-generic name that will differentiate them.
+- Keep all file names in lower case.
+- Consider using PNG images instead of JPEG.
+
+Inside the document:
+
+- The Markdown way of using an image inside a document is:
+  `![Proper description what the image is about](img/document_image_title.png)`
+- Always use a proper description for what the image is about. That way, when a
+  browser fails to show the image, this text will be used as an alternative
+  description
+- If there are consecutive images with little text between them, always add
+  three dashes (`---`) between the image and the text to create a horizontal
+  line for better clarity
+- If a heading is placed right after an image, always add three dashes (`---`)
+  between the image and the heading
+
+## Notes
+
+- Notes should be in italics with the word `Note:` being bold. Use this form:
+  `_**Note:** This is something to note._`. If the note spans across multiple
+  lines it's OK to split the line.
+
+## New features
+
+- Every piece of documentation that comes with a new feature should declare the
+  GitLab version that feature got introduced. Right below the heading add a
+  note: `_**Note:** This feature was introduced in GitLab 8.3_`
+- If possible every feature should have a link to the MR that introduced it.
+  The above note would be then transformed to:
+  `_**Note:** This feature was [introduced][ce-1242] in GitLab 8.3_`, where
+  the [link identifier](#links) is named after the repository (CE) and the MR
+  number
+- If the feature is only in GitLab EE, don't forget to mention it, like:
+  `_**Note:** This feature was introduced in GitLab EE 8.3_`. Otherwise, leave
+  this mention out
+
+## References
+
+- **GitLab Restart:**
+  There are many cases that a restart/reconfigure of GitLab is required. To
+  avoid duplication, link to the special document that can be found in
+  [`doc/administration/restart_gitlab.md`][doc-restart]. Usually the text will
+  read like:
+
+    ```
+    Save the file and [reconfigure GitLab](../administration/restart_gitlab.md)
+    for the changes to take effect.
+    ```
+  If the document you are editing resides in a place other than the GitLab CE/EE
+  `doc/` directory, instead of the relative link, use the full path:
+  `http://doc.gitlab.com/ce/administration/restart_gitlab.html`.
+  Replace `reconfigure` with `restart` where appropriate.
+
+## API
+
+Here is a list of must-have items. Use them in the exact order that appears
+on this document. Further explanation is given below.
+
+- Every method must have the REST API request. For example:
+
+    ```
+    GET /projects/:id/repository/branches
+    ```
+
+- Every method must have a detailed
+  [description of the parameters](#method-description).
+- Every method must have a cURL example.
+- Every method must have a response body (in JSON format).
+
+### Method description
+
+Use the following table headers to describe the methods. Attributes should
+always be in code blocks using backticks (`).
+
+```
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+```
+
+Rendered example:
+
+| Attribute | Type | Required | Description |
+| --------- | ---- | -------- | ----------- |
+| `user`  | string | yes | The GitLab username |
+
+### cURL commands
+
+- Use `https://gitlab.example.com/api/v3/` as an endpoint.
+- Wherever needed use this private token: `9koXpg98eAheJpvBs5tK`.
+- Always put the request first. `GET` is the default so you don't have to
+  include it.
+- Use double quotes to the URL when it includes additional parameters.
+- Prefer to use examples using the private token and don't pass data of
+  username and password.
+
+| Methods | Description |
+| ------- | ----------- |
+| `-H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK"` | Use this method as is, whenever authentication needed |
+| `-X POST`   | Use this method when creating new objects |
+| `-X PUT`    | Use this method when updating existing objects |
+| `-X DELETE` | Use this method when removing existing objects |
+
+### cURL Examples
+
+Below is a set of [cURL][] examples that you can use in the API documentation.
+
+#### Simple cURL command
+
+Get the details of a group:
+
+```bash
+curl -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" https://gitlab.example.com/api/v3/groups/gitlab-org
+```
+
+#### cURL example with parameters passed in the URL
+
+Create a new project under the authenticated user's namespace:
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects?name=foo"
+```
+
+#### Post data using cURL's --data
+
+Instead of using `-X POST` and appending the parameters to the URI, you can use
+cURL's `--data` option. The example below will create a new project `foo` under
+the authenticated user's namespace.
+
+```bash
+curl --data "name=foo" -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects"
+```
+
+#### Post data using JSON content
+
+_**Note:** In this example we create a new group. Watch carefully the single
+and double quotes._
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -H "Content-Type: application/json" --data '{"path": "my-group", "name": "My group"}' https://gitlab.example.com/api/v3/groups
+```
+
+#### Post data using form-data
+
+Instead of using JSON or urlencode you can use multipart/form-data which
+properly handles data encoding:
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -F "title=ssh-key" -F "key=ssh-rsa AAAAB3NzaC1yc2EA..." https://gitlab.example.com/api/v3/users/25/keys
+```
+
+The above example is run by and administrator and will add an SSH public key
+titled ssh-key to user's account which has an id of 25.
+
+#### Escape special characters
+
+Spaces or slashes (`/`) may sometimes result to errors, thus it is recommended
+to escape them when possible. In the example below we create a new issue which
+contains spaces in its title. Observe how spaces are escaped using the `%20`
+ASCII code.
+
+```bash
+curl -X POST -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" "https://gitlab.example.com/api/v3/projects/42/issues?title=Hello%20Dude"
+```
+
+Use `%2F` for slashes (`/`).
+
+#### Pass arrays to API calls
+
+The GitLab API sometimes accepts arrays of strings or integers. For example, to
+restrict the sign-up e-mail domains of a GitLab instance to `*.example.com` and
+`example.net`, you would do something like this:
+
+```bash
+curl -X PUT -H "PRIVATE-TOKEN: 9koXpg98eAheJpvBs5tK" -d "restricted_signup_domains[]=*.example.com" -d "restricted_signup_domains[]=example.net" https://gitlab.example.com/api/v3/application/settings
+```
+
+[cURL]: http://curl.haxx.se/ "cURL website"
+[single spaces]: http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html
+[gfm]: http://doc.gitlab.com/ce/markdown/markdown.html#newlines "GitLab flavored markdown documentation"
+[doc-restart]: ../administration/restart_gitlab.md "GitLab restart documentation"
diff --git a/doc/incoming_email/README.md b/doc/incoming_email/README.md
index 86d205ba7a54a846f72ff8e913440d637f87360b..4cfb840294391e837107d489ec24f68a0acd9d64 100644
--- a/doc/incoming_email/README.md
+++ b/doc/incoming_email/README.md
@@ -74,10 +74,11 @@ To set up a basic Postfix mail server with IMAP access on Ubuntu, follow [these
 
     As mentioned, the part after `+` in the address is ignored, and any email sent here will end up in the mailbox for `incoming@gitlab.example.com`/`gitlab-incoming@gmail.com`.
 
-1. Reconfigure GitLab for the changes to take effect:
+1. Reconfigure GitLab and restart mailroom for the changes to take effect:
 
     ```sh
     sudo gitlab-ctl reconfigure
+    sudo gitlab-ctl restart mailroom
     ```
 
 1. Verify that everything is configured correctly:
diff --git a/doc/incoming_email/postfix.md b/doc/incoming_email/postfix.md
index 18bf3db17445cfeaed9592807af02ae9feb3966e..787d21f7f8fc1c0437a0280dd09c79ee2108a2e5 100644
--- a/doc/incoming_email/postfix.md
+++ b/doc/incoming_email/postfix.md
@@ -84,7 +84,12 @@ The instructions make the assumption that you will be using the email address `i
     quit
     ```
 
-    (Note: The `.` is a literal period on its own line)
+    _**Note:** The `.` is a literal period on its own line._
+
+    _**Note:** If you receive an error after entering `rcpt to: incoming@localhost`
+    then your Postfix `my_network` configuration is not correct. The error will
+    say 'Temporary lookup failure'. See
+    [Configure Postfix to receive email from the Internet](#configure-postfix-to-receive-email-from-the-internet)._
 
 1. Check if the `incoming` user received the email:
     
@@ -131,7 +136,7 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
 1. Test the new setup:
     
     1. Follow steps 1 and 2 of _[Test the out-of-the-box setup](#test-the-out-of-the-box-setup)_.
-    2. Check if the `incoming` user received the email:
+    1. Check if the `incoming` user received the email:
     
         ```sh
         su - incoming
@@ -152,6 +157,12 @@ Courier, which we will install later to add IMAP authentication, requires mailbo
         q
         ```
 
+    _**Note:** If `mail` returns an error `Maildir: Is a directory` then your
+    version of `mail` doesn't support Maildir style mailboxes. Install
+    `heirloom-mailx` by running `sudo apt-get install heirloom-mailx`. Then,
+    try the above steps again, substituting `heirloom-mailx` for the `mail`
+    command._
+
 1. Log out of the `incoming` account and go back to being `root`:
 
     ```sh
diff --git a/doc/install/database_mysql.md b/doc/install/database_mysql.md
index 513ad69ec26d9f07ae5656e2d0955c632fc48622..e51ff5a5de2ea5f9f0f1649a01d14da890fc7f27 100644
--- a/doc/install/database_mysql.md
+++ b/doc/install/database_mysql.md
@@ -8,7 +8,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
 
     # Install the database packages
     sudo apt-get install -y mysql-server mysql-client libmysqlclient-dev
-    
+
     # Ensure you have MySQL version 5.5.14 or later
     mysql --version
 
@@ -31,7 +31,7 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
     # Ensure you can use the InnoDB engine which is necessary to support long indexes
     # If this fails, check your MySQL config files (e.g. `/etc/mysql/*.cnf`, `/etc/mysql/conf.d/*`) for the setting "innodb = off"
     mysql> SET storage_engine=INNODB;
-    
+
     # Create the GitLab production database
     mysql> CREATE DATABASE IF NOT EXISTS `gitlabhq_production` DEFAULT CHARACTER SET `utf8` COLLATE `utf8_unicode_ci`;
 
@@ -52,3 +52,25 @@ We do not recommend using MySQL due to various issues. For example, case [(in)se
     mysql> \q
 
     # You are done installing the database and can go back to the rest of the installation.
+
+## MySQL strings limits
+
+After installation or upgrade, remember to run the `add_limits_mysql` Rake task:
+
+```
+bundle exec rake add_limits_mysql
+```
+
+The `text` type in MySQL has a different size limit than the `text` type in
+PostgreSQL. In MySQL `text` columns are limited to ~65kB, whereas in PostgreSQL
+`text` columns are limited up to ~1GB!
+
+The `add_limits_mysql` Rake task converts some important `text` columns in the
+GitLab database to `longtext` columns, which can persist values of up to 4GB
+(sometimes less if the value contains multibyte characters).
+
+Details can be found in the [PostgreSQL][postgres-text-type] and
+[MySQL][mysql-text-types] manuals.
+
+[postgres-text-type]: http://www.postgresql.org/docs/9.1/static/datatype-character.html
+[mysql-text-types]: http://dev.mysql.com/doc/refman/5.7/en/string-type-overview.html
diff --git a/doc/install/installation.md b/doc/install/installation.md
index dd59733cf4d49792e89023bbe31cc76be193ff2e..4772ed3c566fd6a9d8d8e6c75b62f4ebe1755089 100644
--- a/doc/install/installation.md
+++ b/doc/install/installation.md
@@ -107,9 +107,16 @@ Then select 'Internet Site' and press enter to confirm the hostname.
 
 ## 2. 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.
+_**Note:** The current supported Ruby versions are 2.1.x. Ruby 2.2 and 2.3 are
+currently not supported._
 
-Remove the old Ruby 1.8 if present
+The use of Ruby version managers such as [RVM], [rbenv] or [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:
 
     sudo apt-get remove ruby1.8
 
@@ -135,11 +142,11 @@ 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
-    echo '46eecd290d8803887dec718c691cc243f2175fe0  go1.5.1.linux-amd64.tar.gz' | shasum -c - && \
-      sudo tar -C /usr/local -xzf go1.5.1.linux-amd64.tar.gz
+    curl -O --progress https://storage.googleapis.com/golang/go1.5.3.linux-amd64.tar.gz
+    echo '43afe0c5017e502630b1aea4d44b8a7f059bf60d7f29dfd58db454d4e4e0ae53  go1.5.3.linux-amd64.tar.gz' | shasum -c - && \
+      sudo tar -C /usr/local -xzf go1.5.3.linux-amd64.tar.gz
     sudo ln -sf /usr/local/go/bin/{go,godoc,gofmt} /usr/local/bin/
-    rm go1.5.1.linux-amd64.tar.gz
+    rm go1.5.3.linux-amd64.tar.gz
 
 ## 4. System Users
 
@@ -231,9 +238,9 @@ sudo usermod -aG redis git
 ### Clone the Source
 
     # Clone GitLab repository
-    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-3-stable gitlab
+    sudo -u git -H git clone https://gitlab.com/gitlab-org/gitlab-ce.git -b 8-4-stable gitlab
 
-**Note:** You can change `8-3-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
+**Note:** You can change `8-4-stable` to `master` if you want the *bleeding edge* version, but never install master on a production server!
 
 ### Configure It
 
@@ -348,7 +355,7 @@ GitLab Shell is an SSH access and repository management software developed speci
     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.5.1
+    sudo -u git -H git checkout 0.5.4
     sudo -u git -H make
 
 ### Initialize Database and Activate Advanced Features
@@ -555,3 +562,7 @@ this is likely due to an outdated Nginx or Apache configuration, or a missing or
 misconfigured gitlab-workhorse instance. Double-check that you've
 [installed Go](#3-go), [installed gitlab-workhorse](#install-gitlab-workhorse),
 and correctly [configured Nginx](#site-configuration).
+
+[RVM]: https://rvm.io/ "RVM Homepage"
+[rbenv]: https://github.com/sstephenson/rbenv "rbenv on GitHub"
+[chruby]: https://github.com/postmodern/chruby "chruby on GitHub"
diff --git a/doc/install/requirements.md b/doc/install/requirements.md
index c0ccdd374586e29ae5ec53939c5fde3b968c3df0..c0425f27ab17e3f9a7e6db223d552fab372f51de 100644
--- a/doc/install/requirements.md
+++ b/doc/install/requirements.md
@@ -32,15 +32,18 @@ Please consider using a virtual machine to run GitLab.
 
 ## Ruby versions
 
-GitLab requires Ruby (MRI) 2.1
+GitLab requires Ruby (MRI) 2.1.x and currently does not work with versions 2.2
+and 2.3.
+
 You will have to use the standard MRI implementation of Ruby.
-We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab needs several Gems that have native extensions.
+We love [JRuby](http://jruby.org/) and [Rubinius](http://rubini.us/) but GitLab
+needs several Gems that have native extensions.
 
 ## Hardware requirements
 
 ### Storage
 
-The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repos combined take up. 
+The necessary hard drive space largely depends on the size of the repos you want to store in GitLab but as a *rule of thumb* you should have at least as much free space as all your repos combined take up.
 
 If you want to be flexible about growing your hard drive space in the future consider mounting it using LVM so you can add more hard drives when you need them.
 
@@ -109,4 +112,4 @@ On a very active server (10,000 active users) the Sidekiq process can use 1GB+ o
 - Firefox (Latest released version and [latest ESR version](https://www.mozilla.org/en-US/firefox/organizations/))
 - Safari 7+ (known problem: required fields in html5 do not work)
 - Opera (Latest released version)
-- Internet Explorer (IE) 10+ but please make sure that you have the `Compatibility View` mode disabled.
\ No newline at end of file
+- Internet Explorer (IE) 10+ but please make sure that you have the `Compatibility View` mode disabled.
diff --git a/doc/integration/ldap.md b/doc/integration/ldap.md
index 845f588f913808451ceef55dfb823ddeabcf663e..f256477196b50f4af0b4c3215e86e5a877facaf4 100644
--- a/doc/integration/ldap.md
+++ b/doc/integration/ldap.md
@@ -48,6 +48,11 @@ main: # 'main' is the GitLab 'provider ID' of this LDAP server
   bind_dn: '_the_full_dn_of_the_user_you_will_bind_with'
   password: '_the_password_of_the_bind_user'
 
+  # Set a timeout, in seconds, for LDAP queries. This helps avoid blocking
+  # a request if the LDAP server becomes unresponsive.
+  # A value of 0 means there is no timeout.
+  timeout: 10
+
   # This setting specifies if LDAP server is Active Directory LDAP server.
   # For non AD servers it skips the AD specific queries.
   # If your LDAP server is not AD, set this to false.
diff --git a/doc/integration/saml.md b/doc/integration/saml.md
index 1632e42f701925afcd5400423566d4699a14a75d..8841dbdb7c67f69700a878fca731dfffc40f3dbb 100644
--- a/doc/integration/saml.md
+++ b/doc/integration/saml.md
@@ -78,6 +78,18 @@ On the sign in page there should now be a SAML button below the regular sign in
 
 ## Troubleshooting
 
-If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page, this likely indicates that GitLab could not get the email address for the SAML user.
+If you see a "500 error" in GitLab when you are redirected back from the SAML sign in page,
+this likely indicates that GitLab could not get the email address for the SAML user.
 
-Make sure the IdP provides a claim containing the user's email address, using claim name 'email' or 'mail'. The email will be used to automatically generate the GitLab username.
\ No newline at end of file
+Make sure the IdP provides a claim containing the user's email address, using claim name
+'email' or 'mail'. The email will be used to automatically generate the GitLab username.
+
+If after signing in into your SAML server you are redirected back to the sign in page and
+no error is displayed, check your `production.log` file. It will most likely contain the
+message `Can't verify CSRF token authenticity`. This means that there is an error during
+the SAML request, but this error never reaches GitLab due to the CSRF check.
+
+To bypass this you can add `skip_before_action :verify_authenticity_token` to the
+`omniauth_callbacks_controller.rb` file. This will allow the error to hit GitLab,
+where it can then be seen in the usual logs, or as a flash message in the login
+screen.
\ No newline at end of file
diff --git a/doc/integration/shibboleth.md b/doc/integration/shibboleth.md
index 6258e5f1030980980b9188cebb2d2c2031fc554a..a0be3dd4e5cc3f3ee63cf84e1dd966aa639fbfbd 100644
--- a/doc/integration/shibboleth.md
+++ b/doc/integration/shibboleth.md
@@ -1,8 +1,8 @@
 # Shibboleth OmniAuth Provider
 
-This documentation is for enabling shibboleth with gitlab-omnibus package.
+This documentation is for enabling shibboleth with omnibus-gitlab package.
 
-In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in gitlab-omnibus package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider.
+In order to enable Shibboleth support in gitlab we need to use Apache instead of Nginx (It may be possible to use Nginx, however I did not found way to easily configure Nginx that is bundled in omnibus-gitlab package). Apache uses mod_shib2 module for shibboleth authentication and can pass attributes as headers to omniauth-shibboleth provider.
 
 
 To enable the Shibboleth OmniAuth provider you must:
diff --git a/doc/monitoring/performance/gitlab_configuration.md b/doc/monitoring/performance/gitlab_configuration.md
new file mode 100644
index 0000000000000000000000000000000000000000..b856e7935a3955df647f00abfed40fa1f4efc3aa
--- /dev/null
+++ b/doc/monitoring/performance/gitlab_configuration.md
@@ -0,0 +1,39 @@
+# GitLab Configuration
+
+GitLab Performance Monitoring is disabled by default. To enable it and change any of its
+settings, navigate to the Admin area in **Settings > Metrics**
+(`/admin/application_settings`).
+
+The minimum required settings you need to set are the InfluxDB host and port.
+Make sure _Enable InfluxDB Metrics_ is checked and hit **Save** to save the
+changes.
+
+---
+
+![GitLab Performance Monitoring Admin Settings](img/metrics_gitlab_configuration_settings.png)
+
+---
+
+Finally, a restart of all GitLab processes is required for the changes to take
+effect:
+
+```bash
+# For Omnibus installations
+sudo gitlab-ctl restart
+
+# For installations from source
+sudo service gitlab restart
+```
+
+## Pending Migrations
+
+When any migrations are pending, the metrics are disabled until the migrations
+have been performed.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [InfluxDB Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
diff --git a/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png
new file mode 100644
index 0000000000000000000000000000000000000000..14d82b6ac98c5c942b93a83d17f6f82987a5c7b3
Binary files /dev/null and b/doc/monitoring/performance/img/metrics_gitlab_configuration_settings.png differ
diff --git a/doc/monitoring/performance/influxdb_configuration.md b/doc/monitoring/performance/influxdb_configuration.md
new file mode 100644
index 0000000000000000000000000000000000000000..3a2b598b78f5be80b8469adf0c4c6925a3d10b29
--- /dev/null
+++ b/doc/monitoring/performance/influxdb_configuration.md
@@ -0,0 +1,192 @@
+# InfluxDB Configuration
+
+The default settings provided by [InfluxDB] are not sufficient for a high traffic
+GitLab environment. The settings discussed in this document are based on the
+settings GitLab uses for GitLab.com, depending on your own needs you may need to
+further adjust them.
+
+If you are intending to run InfluxDB on the same server as GitLab, make sure
+you have plenty of RAM since InfluxDB can use quite a bit depending on traffic.
+
+Unless you are going with a budget setup, it's advised to run it separately.
+
+## Requirements
+
+- InfluxDB 0.9.5 or newer
+- A fairly modern version of Linux
+- At least 4GB of RAM
+- At least 10GB of storage for InfluxDB data
+
+Note that the RAM and storage requirements can differ greatly depending on the
+amount of data received/stored. To limit the amount of stored data users can
+look into [InfluxDB Retention Policies][influxdb-retention].
+
+## Installation
+
+Installing InfluxDB is out of the scope of this document. Please refer to the
+[InfluxDB documentation].
+
+## InfluxDB Server Settings
+
+Since InfluxDB has many settings that users may wish to customize themselves
+(e.g. what port to run InfluxDB on), we'll only cover the essentials.
+
+The configuration file in question is usually located at
+`/etc/influxdb/influxdb.conf`. Whenever you make a change in this file,
+InfluxDB needs to be restarted.
+
+### Storage Engine
+
+InfluxDB comes with different storage engines and as of InfluxDB 0.9.5 a new
+storage engine is available, called [TSM Tree]. All users **must** use the new
+`tsm1` storage engine as this [will be the default engine][tsm1-commit] in
+upcoming InfluxDB releases.
+
+Make sure you have the following in your configuration file:
+
+```
+[data]
+  dir = "/var/lib/influxdb/data"
+  engine = "tsm1"
+```
+
+### Admin Panel
+
+Production environments should have the InfluxDB admin panel **disabled**. This
+feature can be disabled by adding the following to your InfluxDB configuration
+file:
+
+```
+[admin]
+  enabled = false
+```
+
+### HTTP
+
+HTTP is required when using the [InfluxDB CLI] or other tools such as Grafana,
+thus it should be enabled. When enabling make sure to _also_ enable
+authentication:
+
+```
+[http]
+  enabled = true
+  auth-enabled = true
+```
+
+_**Note:** Before you enable authentication, you might want to [create an
+admin user](#create-a-new-admin-user)._
+
+### UDP
+
+GitLab writes data to InfluxDB via UDP and thus this must be enabled. Enabling
+UDP can be done using the following settings:
+
+```
+[[udp]]
+  enabled = true
+  bind-address = ":8089"
+  database = "gitlab"
+  batch-size = 1000
+  batch-pending = 5
+  batch-timeout = "1s"
+  read-buffer = 209715200
+```
+
+This does the following:
+
+1. Enable UDP and bind it to port 8089 for all addresses.
+2. Store any data received in the "gitlab" database.
+3. Define a batch of points to be 1000 points in size and allow a maximum of
+   5 batches _or_ flush them automatically after 1 second.
+4. Define a UDP read buffer size of 200 MB.
+
+One of the most important settings here is the UDP read buffer size as if this
+value is set too low, packets will be dropped. You must also make sure the OS
+buffer size is set to the same value, the default value is almost never enough.
+
+To set the OS buffer size to 200 MB, on Linux you can run the following command:
+
+```bash
+sysctl -w net.core.rmem_max=209715200
+```
+
+To make this permanent, add the following to `/etc/sysctl.conf` and restart the
+server:
+
+```bash
+net.core.rmem_max=209715200
+```
+
+It is **very important** to make sure the buffer sizes are large enough to
+handle all data sent to InfluxDB as otherwise you _will_ lose data. The above
+buffer sizes are based on the traffic for GitLab.com. Depending on the amount of
+traffic, users may be able to use a smaller buffer size, but we highly recommend
+using _at least_ 100 MB.
+
+When enabling UDP, users should take care to not expose the port to the public,
+as doing so will allow anybody to write data into your InfluxDB database (as
+[InfluxDB's UDP protocol][udp] doesn't support authentication). We recommend either
+whitelisting the allowed IP addresses/ranges, or setting up a VLAN and only
+allowing traffic from members of said VLAN.
+
+## Create a new admin user
+
+If you want to [enable authentication](#http), you might want to [create an
+admin user][influx-admin]:
+
+```
+influx -execute "CREATE USER jeff WITH PASSWORD '1234' WITH ALL PRIVILEGES"
+```
+
+## Create the `gitlab` database
+
+Once you get InfluxDB up and running, you need to create a database for GitLab.
+Make sure you have changed the [storage engine](#storage-engine) to `tsm1`
+before creating a database.
+
+_**Note:** If you [created an admin user](#create-a-new-admin-user) and enabled
+[HTTP authentication](#http), remember to append the username (`-username <username>`)
+and password (`-password <password>`)  you set earlier to the commands below._
+
+Run the following command to create a database named `gitlab`:
+
+```bash
+influx -execute 'CREATE DATABASE gitlab'
+```
+
+The name **must** be `gitlab`, do not use any other name.
+
+Next, make sure that the database was successfully created:
+
+```bash
+influx -execute 'SHOW DATABASES'
+```
+
+The output should be similar to:
+
+```
+name: databases
+---------------
+name
+_internal
+gitlab
+```
+
+That's it! Now your GitLab instance should send data to InfluxDB.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+
+[influxdb-retention]: https://docs.influxdata.com/influxdb/v0.9/query_language/database_management/#retention-policy-management
+[influxdb documentation]: https://docs.influxdata.com/influxdb/v0.9/
+[influxdb cli]: https://docs.influxdata.com/influxdb/v0.9/tools/shell/
+[udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
+[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[tsm tree]: https://influxdata.com/blog/new-storage-engine-time-structured-merge-tree/
+[tsm1-commit]: https://github.com/influxdata/influxdb/commit/15d723dc77651bac83e09e2b1c94be480966cb0d
+[influx-admin]: https://docs.influxdata.com/influxdb/v0.9/administration/authentication_and_authorization/#create-a-new-admin-user
diff --git a/doc/monitoring/performance/influxdb_schema.md b/doc/monitoring/performance/influxdb_schema.md
new file mode 100644
index 0000000000000000000000000000000000000000..a5a8aebd2d1381643cb27f0c1375a9e415bfa969
--- /dev/null
+++ b/doc/monitoring/performance/influxdb_schema.md
@@ -0,0 +1,87 @@
+# InfluxDB Schema
+
+The following measurements are currently stored in InfluxDB:
+
+- `PROCESS_file_descriptors`
+- `PROCESS_gc_statistics`
+- `PROCESS_memory_usage`
+- `PROCESS_method_calls`
+- `PROCESS_object_counts`
+- `PROCESS_transactions`
+- `PROCESS_views`
+
+Here, `PROCESS` is replaced with either `rails` or `sidekiq` depending on the
+process type. In all series, any form of duration is stored in milliseconds.
+
+## PROCESS_file_descriptors
+
+This measurement contains the number of open file descriptors over time. The
+value field `value` contains the number of descriptors.
+
+## PROCESS_gc_statistics
+
+This measurement contains Ruby garbage collection statistics such as the amount
+of minor/major GC runs (relative to the last sampling interval), the time spent
+in garbage collection cycles, and all fields/values returned by `GC.stat`.
+
+## PROCESS_memory_usage
+
+This measurement contains the process' memory usage (in bytes) over time. The
+value field `value` contains the number of bytes.
+
+## PROCESS_method_calls
+
+This measurement contains the methods called during a transaction along with
+their duration, and a name of the transaction action that invoked the method (if
+available). The method call duration is stored in the value field `duration`,
+while the method name is stored in the tag `method`. The tag `action` contains
+the full name of the transaction action. Both the `method` and `action` fields
+are in the following format:
+
+```
+ClassName#method_name
+```
+
+For example, a method called by the `show` method in the `UsersController` class
+would have `action` set to `UsersController#show`.
+
+## PROCESS_object_counts
+
+This measurement is used to store retained Ruby objects (per class) and the
+amount of retained objects. The number of objects is stored in the `count` value
+field while the class name is stored in the `type` tag.
+
+## PROCESS_transactions
+
+This measurement is used to store basic transaction details such as the time it
+took to complete a transaction, how much time was spent in SQL queries, etc. The
+following value fields are available:
+
+| Value | Description |
+| ----- | ----------- |
+| `duration`  | The total duration of the transaction |
+| `allocated_memory` | The amount of bytes allocated while the transaction was running. This value is only reliable when using single-threaded application servers |
+| `method_duration` | The total time spent in method calls |
+| `sql_duration` | The total time spent in SQL queries |
+| `view_duration` | The total time spent in views |
+
+## PROCESS_views
+
+This measurement is used to store view rendering timings for a transaction. The
+following value fields are available:
+
+| Value | Description |
+| ----- | ----------- |
+| `duration` | The rendering time of the view |
+| `view` | The path of the view, relative to the application's root directory |
+
+The `action` tag contains the action name of the transaction that rendered the
+view.
+
+---
+
+Read more on:
+
+- [Introduction to GitLab Performance Monitoring](introduction.md)
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Configuration](influxdb_configuration.md)
diff --git a/doc/monitoring/performance/introduction.md b/doc/monitoring/performance/introduction.md
new file mode 100644
index 0000000000000000000000000000000000000000..f2460d3130203f33379701a01a408f54a246ab44
--- /dev/null
+++ b/doc/monitoring/performance/introduction.md
@@ -0,0 +1,64 @@
+# GitLab Performance Monitoring
+
+GitLab comes with its own application performance measuring system as of GitLab
+8.4, simply called "GitLab Performance Monitoring". GitLab Performance Monitoring is available in both the
+Community and Enterprise editions.
+
+Apart from this introduction, you are advised to read through the following
+documents in order to understand and properly configure GitLab Performance Monitoring:
+
+- [GitLab Configuration](gitlab_configuration.md)
+- [InfluxDB Configuration](influxdb_configuration.md)
+- [InfluxDB Schema](influxdb_schema.md)
+
+## Introduction to GitLab Performance Monitoring
+
+GitLab Performance Monitoring makes it possible to measure a wide variety of statistics
+including (but not limited to):
+
+- The time it took to complete a transaction (a web request or Sidekiq job).
+- The time spent in running SQL queries and rendering HAML views.
+- The time spent executing (instrumented) Ruby methods.
+- Ruby object allocations, and retained objects in particular.
+- System statistics such as the process' memory usage and open file descriptors.
+- Ruby garbage collection statistics.
+
+Metrics data is written to [InfluxDB][influxdb] over [UDP][influxdb-udp]. Stored
+data can be visualized using [Grafana][grafana] or any other application that
+supports reading data from InfluxDB. Alternatively data can be queried using the
+InfluxDB CLI.
+
+## Metric Types
+
+Two types of metrics are collected:
+
+1. Transaction specific metrics.
+1. Sampled metrics, collected at a certain interval in a separate thread.
+
+### Transaction Metrics
+
+Transaction metrics are metrics that can be associated with a single
+transaction. This includes statistics such as the transaction duration, timings
+of any executed SQL queries, time spent rendering HAML views, etc. These metrics
+are collected for every Rack request and Sidekiq job processed.
+
+### Sampled Metrics
+
+Sampled metrics are metrics that can't be associated with a single transaction.
+Examples include garbage collection statistics and retained Ruby objects. These
+metrics are collected at a regular interval. This interval is made up out of two
+parts:
+
+1. A user defined interval.
+1. A randomly generated offset added on top of the interval, the same offset
+   can't be used twice in a row.
+
+The actual interval can be anywhere between a half of the defined interval and a
+half above the interval. For example, for a user defined interval of 15 seconds
+the actual interval can be anywhere between 7.5 and 22.5. The interval is
+re-generated for every sampling run instead of being generated once and re-used
+for the duration of the process' lifetime.
+
+[influxdb]: https://influxdata.com/time-series-platform/influxdb/
+[influxdb-udp]: https://docs.influxdata.com/influxdb/v0.9/write_protocols/udp/
+[grafana]: http://grafana.org/
diff --git a/doc/release/patch.md b/doc/release/patch.md
index 3022e375acac13590d1a959043fd2dafd1a2705a..1c921439156aa118ec7895cfa179ba99db104b8d 100644
--- a/doc/release/patch.md
+++ b/doc/release/patch.md
@@ -24,7 +24,7 @@ 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
+- omnibus-gitlab
   - [ ] `x.y.z+ee.0`
   - [ ] `x.y.z+ce.0`
 - [ ] Deploy
diff --git a/doc/system_hooks/system_hooks.md b/doc/system_hooks/system_hooks.md
index 49f98ded0464ee94b11848c899b9131d535f3690..612376e3a49a5515967f810bf38ab5e5e8acaaf6 100644
--- a/doc/system_hooks/system_hooks.md
+++ b/doc/system_hooks/system_hooks.md
@@ -96,6 +96,7 @@ X-Gitlab-Event: System Hook
  "project_path_with_namespace": "jsmith/storecloud",
                   "user_email": "johnsmith@gmail.com",
                    "user_name": "John Smith",
+               "user_username": "johnsmith",
                      "user_id": 41,
           "project_visibility": "private",
 }
@@ -115,6 +116,7 @@ X-Gitlab-Event: System Hook
  "project_path_with_namespace": "jsmith/storecloud",
                   "user_email": "johnsmith@gmail.com",
                    "user_name": "John Smith",
+               "user_username": "johnsmith",
                      "user_id": 41,
           "project_visibility": "private",
 }
@@ -129,6 +131,7 @@ X-Gitlab-Event: System Hook
         "email": "js@gitlabhq.com",
    "event_name": "user_create",
          "name": "John Smith",
+     "username": "js",
       "user_id": 41
 }
 ```
@@ -142,6 +145,7 @@ X-Gitlab-Event: System Hook
         "email": "js@gitlabhq.com",
    "event_name": "user_destroy",
          "name": "John Smith",
+     "username": "js",
       "user_id": 41
 }
 ```
@@ -215,6 +219,7 @@ X-Gitlab-Event: System Hook
     "group_path": "storecloud",
     "user_email": "johnsmith@gmail.com",
      "user_name": "John Smith",
+ "user_username": "johnsmith",
        "user_id": 41
 }
 ```
@@ -231,6 +236,7 @@ X-Gitlab-Event: System Hook
     "group_path": "storecloud",
     "user_email": "johnsmith@gmail.com",
      "user_name": "John Smith",
+ "user_username": "johnsmith",
        "user_id": 41
 }
 ```
diff --git a/doc/update/8.2-to-8.3.md b/doc/update/8.2-to-8.3.md
index 3748941b7815a93c9de94fe1ace36496e1d01b29..2ca4e1f37702308a506594766e9c70221605be6e 100644
--- a/doc/update/8.2-to-8.3.md
+++ b/doc/update/8.2-to-8.3.md
@@ -78,7 +78,7 @@ which should already be on your system from GitLab 8.1.
 ```bash
 cd /home/git/gitlab-workhorse
 sudo -u git -H git fetch --all
-sudo -u git -H git checkout 0.5.1
+sudo -u git -H git checkout 0.5.4
 sudo -u git -H make
 ```
 
diff --git a/doc/update/8.3-to-8.4.md b/doc/update/8.3-to-8.4.md
new file mode 100644
index 0000000000000000000000000000000000000000..bf80f66d004da92cfacc19663d1de20496944f33
--- /dev/null
+++ b/doc/update/8.3-to-8.4.md
@@ -0,0 +1,145 @@
+# From 8.3 to 8.4
+
+### 1. Stop server
+
+    sudo service gitlab stop
+
+### 2. Backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:create RAILS_ENV=production
+```
+
+### 3. Get latest code
+
+```bash
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout -- db/schema.rb # local changes will be restored automatically
+```
+
+For GitLab Community Edition:
+
+```bash
+sudo -u git -H git checkout 8-4-stable
+```
+
+OR
+
+For GitLab Enterprise Edition:
+
+```bash
+sudo -u git -H git checkout 8-4-stable-ee
+```
+
+### 4. Update gitlab-shell
+
+```bash
+cd /home/git/gitlab-shell
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout v2.6.9
+```
+
+### 5. Update gitlab-workhorse
+
+Install and compile gitlab-workhorse. This requires [Go 1.5](https://golang.org/dl)
+which should already be on your system from GitLab 8.1.
+
+```bash
+cd /home/git/gitlab-workhorse
+sudo -u git -H git fetch --all
+sudo -u git -H git checkout 0.6.1
+sudo -u git -H make
+```
+
+### 6. Install libs, migrations, etc.
+
+```bash
+cd /home/git/gitlab
+
+# MySQL installations (note: the line below states '--without postgres')
+sudo -u git -H bundle install --without postgres development test --deployment
+
+# PostgreSQL installations (note: the line below states '--without mysql')
+sudo -u git -H bundle install --without mysql development test --deployment
+
+# Run database migrations
+sudo -u git -H bundle exec rake db:migrate RAILS_ENV=production
+
+# Clean up assets and cache
+sudo -u git -H bundle exec rake assets:clean assets:precompile cache:clear RAILS_ENV=production
+
+```
+
+### 7. Update configuration files
+
+#### New configuration options for `gitlab.yml`
+
+There are new configuration options available for [`gitlab.yml`](config/gitlab.yml.example). View them with the command below and apply them manually to your current `gitlab.yml`:
+
+```sh
+git diff origin/8-3-stable:config/gitlab.yml.example origin/8-4-stable:config/gitlab.yml.example
+```
+
+#### Nginx configuration
+
+GitLab 8.3 introduced major changes in the NGINX configuration. Ensure you're
+still up-to-date with the latest changes:
+
+```sh
+# For HTTPS configurations
+git diff origin/8-3-stable:lib/support/nginx/gitlab-ssl origin/8-4-stable:lib/support/nginx/gitlab-ssl
+
+# For HTTP configurations
+git diff origin/8-3-stable:lib/support/nginx/gitlab origin/8-4-stable:lib/support/nginx/gitlab
+```
+
+If you are using Apache instead of NGINX please see the updated [Apache templates].
+Also note that because Apache does not support upstreams behind Unix sockets you
+will need to let gitlab-workhorse listen on a TCP port. You can do this
+via [/etc/default/gitlab].
+
+[Apache templates]: https://gitlab.com/gitlab-org/gitlab-recipes/tree/master/web-server/apache
+[/etc/default/gitlab]: https://gitlab.com/gitlab-org/gitlab-ce/blob/8-4-stable/lib/support/init.d/gitlab.default.example#L34
+
+#### Init script
+
+We updated the init script for GitLab in order to set a specific PATH for gitlab-workhorse.
+
+```
+cd /home/git/gitlab
+sudo cp lib/support/init.d/gitlab /etc/init.d/gitlab
+```
+
+### 8. Start application
+
+    sudo service gitlab start
+    sudo service nginx restart
+
+### 9. 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
+
+To make sure you didn't miss anything run a more thorough check:
+
+    sudo -u git -H bundle exec rake gitlab:check RAILS_ENV=production
+
+If all items are green, then congratulations, the upgrade is complete!
+
+## Things went south? Revert to previous version (8.3)
+
+### 1. Revert the code to the previous version
+
+Follow the [upgrade guide from 8.2 to 8.3](8.2-to-8.3.md), except for the
+database migration (the backup is already migrated to the previous version).
+
+### 2. Restore from the backup
+
+```bash
+cd /home/git/gitlab
+sudo -u git -H bundle exec rake gitlab:backup:restore RAILS_ENV=production
+```
+
+If you have more than one backup `*.tar` file(s) please add `BACKUP=timestamp_of_backup` to the command above.
diff --git a/doc/update/README.md b/doc/update/README.md
index 0472537eeb5ff9532cece6381f30b9e451363bb0..109d5de3fa2e55ceb3c3d0d9fdac639204ab06ea 100644
--- a/doc/update/README.md
+++ b/doc/update/README.md
@@ -14,3 +14,4 @@ Depending on the installation method and your GitLab version, there are multiple
 ## Miscellaneous
 
 - [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating your database from MySQL to PostgreSQL.
+- [MySQL installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/database_mysql.md) contains additional information about configuring GitLab to work with a MySQL database.
diff --git a/doc/update/patch_versions.md b/doc/update/patch_versions.md
index c19ee49f9e035bdcdfff57a3b913d3570c6b6d93..a10e62877ba89de5a1228006fff608162115d1d0 100644
--- a/doc/update/patch_versions.md
+++ b/doc/update/patch_versions.md
@@ -48,6 +48,7 @@ sudo -u git -H git checkout v`cat /home/git/gitlab/GITLAB_SHELL_VERSION` -b v`ca
 cd /home/git/gitlab-workhorse
 sudo -u git -H git fetch
 sudo -u git -H git checkout `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION` -b `cat /home/git/gitlab/GITLAB_WORKHORSE_VERSION`
+sudo -u git -H make
 ```
 
 ### 5. Install libs, migrations, etc.
diff --git a/doc/web_hooks/web_hooks.md b/doc/web_hooks/web_hooks.md
index 6420d65cf1bcb0eed818a490f6dacb892bd0c290..c29037e89c2761d092d287ada510918ff9bb14cb 100644
--- a/doc/web_hooks/web_hooks.md
+++ b/doc/web_hooks/web_hooks.md
@@ -56,7 +56,7 @@ X-Gitlab-Event: Push Hook
       "author": {
         "name": "Jordi Mallach",
         "email": "jordi@softcatala.org"
-      }
+      },
       "added": ["CHANGELOG"],
       "modified": ["app/controller/application.rb"],
       "removed": []
diff --git a/doc/workflow/README.md b/doc/workflow/README.md
index 3651b55f438ee3c9d45e56e6daf69dd5e8c6a3b3..bf62ab4105329e8a671ef681ac2b78c566d8cdbf 100644
--- a/doc/workflow/README.md
+++ b/doc/workflow/README.md
@@ -6,6 +6,7 @@
 - [GitLab Flow](gitlab_flow.md)
 - [Groups](groups.md)
 - [Keyboard shortcuts](shortcuts.md)
+- [File finder](file_finder.md)
 - [Labels](labels.md)
 - [Notification emails](notifications.md)
 - [Project Features](project_features.md)
diff --git a/doc/workflow/file_finder.md b/doc/workflow/file_finder.md
new file mode 100644
index 0000000000000000000000000000000000000000..52ac5f032a5cd6f2c187afa774f8a65834903c04
--- /dev/null
+++ b/doc/workflow/file_finder.md
@@ -0,0 +1,42 @@
+# File finder
+
+_**Note:** This feature was [introduced][gh-9889] in GitLab 8.4._
+
+---
+
+The file finder feature allows you to quickly shortcut your way when you are
+searching for a file in a repository using the GitLab UI.
+
+You can find the **Find File** button when in the **Files** section of a
+project.
+
+![Find file button](img/file_finder_find_button.png)
+
+---
+
+For the more lazy, there is a [shortcut button](shortcuts.md) as well.
+
+Go the **Files** section of a project and press `t` on your keyboard to launch
+the search function. Start typing what you are searching for and watch the
+magic being unfold. With the up/down arrows, you go up and down the results,
+with `Esc` you close the search and go back to **Files**.
+
+## How it works
+
+The File finder feature is powered by the [Fuzzy filter] library.
+
+It implements a fuzzy search with highlight, and tries to provide intuitive
+results by recognizing patterns that people use while searching.
+
+For example, consider the [GitLab CE repository][ce] and that we want to open
+the `app/controllers/admin/deploy_keys_controller.rb` file.
+
+Using fuzzy search, we start by typing letters that get us closer to the file.
+
+**Protip:** To narrow down your search, include `/` in your search terms.
+
+![Find file button](img/file_finder_find_file.png)
+
+[gh-9889]: https://github.com/gitlabhq/gitlabhq/pull/9889 "File finder pull request"
+[fuzzy filter]: https://github.com/jeancroy/fuzzaldrin-plus "fuzzaldrin-plus on GitHub"
+[ce]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master "GitLab CE repository"
diff --git a/doc/workflow/img/file_finder_find_button.png b/doc/workflow/img/file_finder_find_button.png
new file mode 100644
index 0000000000000000000000000000000000000000..c5005d0d7cab1799875f75a55f74774560eb9a65
Binary files /dev/null and b/doc/workflow/img/file_finder_find_button.png differ
diff --git a/doc/workflow/img/file_finder_find_file.png b/doc/workflow/img/file_finder_find_file.png
new file mode 100644
index 0000000000000000000000000000000000000000..58500f4c163fbbe6e7889eb69a975aec95bfe2f3
Binary files /dev/null and b/doc/workflow/img/file_finder_find_file.png differ
diff --git a/doc/workflow/importing/github_importer/importer.png b/doc/workflow/importing/github_importer/importer.png
deleted file mode 100644
index 57636717571a17ea17ee5cfdabbd4408597fd668..0000000000000000000000000000000000000000
Binary files a/doc/workflow/importing/github_importer/importer.png and /dev/null differ
diff --git a/doc/workflow/importing/github_importer/new_project_page.png b/doc/workflow/importing/github_importer/new_project_page.png
deleted file mode 100644
index 002f22d81d7d07eeece0deaec177b0dbc70745f6..0000000000000000000000000000000000000000
Binary files a/doc/workflow/importing/github_importer/new_project_page.png and /dev/null differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_importer.png b/doc/workflow/importing/img/import_projects_from_github_importer.png
new file mode 100644
index 0000000000000000000000000000000000000000..f744dc06f8195c7944e16053a8ce102741228633
Binary files /dev/null and b/doc/workflow/importing/img/import_projects_from_github_importer.png differ
diff --git a/doc/workflow/importing/img/import_projects_from_github_new_project_page.png b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png
new file mode 100644
index 0000000000000000000000000000000000000000..86be35acb3735f00b2d6b86c098bc37771892eca
Binary files /dev/null and b/doc/workflow/importing/img/import_projects_from_github_new_project_page.png differ
diff --git a/doc/workflow/importing/import_projects_from_github.md b/doc/workflow/importing/import_projects_from_github.md
index 2027a055c37bfea625c66e404502c7289581964e..f693f430a4235e6e0fc0aaa4eb6ae42f43499654 100644
--- a/doc/workflow/importing/import_projects_from_github.md
+++ b/doc/workflow/importing/import_projects_from_github.md
@@ -1,20 +1,44 @@
 # Import your project from GitHub to GitLab
 
-It takes just a couple of steps to import your existing GitHub projects to GitLab. Keep in mind that it is possible only if
-GitHub support is enabled on your GitLab instance. You can read more about GitHub support [here](http://doc.gitlab.com/ce/integration/github.html)
+_**Note:** In order to enable the GitHub import setting, you should first
+enable the [GitHub integration][gh-import] in your GitLab instance._
 
-If you want to import from a GitHub Enterprise instance, you need to use GitLab Enterprise; please see the [EE docs for the GitHub integration](http://doc.gitlab.com/ee/integration/github.html).
+At its current state, GitHub importer can import:
 
-* Sign in to GitLab.com and go to your dashboard.
-* To get to the importer page, you need to go to the "New project" page.
+- the repository description (introduced in GitLab 7.7)
+- the git repository data (introduced in GitLab 7.7)
+- the issues (introduced in GitLab 7.7)
+- the pull requests (introduced in GitLab 8.4)
+- the wiki pages (introduced in GitLab 8.4)
 
-![New project page](github_importer/new_project_page.png)
+It is not yet possible to import your labels, milestones and cross-repository
+pull requests (those from forks). We are working on improving this in the near
+future.
 
-* Click on the "Import project from GitHub" link and you will be redirected to GitHub for permission to access your projects. After accepting, you'll be automatically redirected to the importer.
+The importer page is visible when you [create a new project][new-project].
+Click on the **GitHub** link and you will be redirected to GitHub for
+permission to access your projects. After accepting, you'll be automatically
+redirected to the importer.
 
-![Importer page](github_importer/importer.png)
+![New project page on GitLab](img/import_projects_from_github_new_project_page.png)
 
-* To import a project, you can simple click "Add". The importer will import your repository, issues, and pull requests. Once the importer is done, a new GitLab project will be created with your imported data.
+---
 
-### Note
-When you import your projects from GitHub, it is not possible to keep your labels, milestones, and cross-repository pull requests. We are working on improving this in the near future.
+While at the GitHub importer page, you can see the import statuses of your
+GitHub projects. Those that are being imported will show a _started_ status,
+those already imported will be green, whereas those that are not yet imported
+have an **Import** button on the right side of the table. If you want, you can
+import all your GitHub projects in one go by hitting **Import all projects**
+in the upper left corner.
+
+![GitHub importer page](img/import_projects_from_github_importer.png)
+
+---
+
+The importer will create any new namespaces if they don't exist or in the
+case the namespace is taken, the project will be imported on the user's
+namespace.
+
+[gh-import]: ../../integration/github.md "GitHub integration"
+[ee-gh]: http://doc.gitlab.com/ee/integration/github.html "GitHub integration for GitLab EE"
+[new-project]: ../../gitlab-basics/create-project.md "How to create a new project in GitLab"
diff --git a/doc/workflow/protected_branches.md b/doc/workflow/protected_branches.md
index 0adf9f8e3e8861a44cb226d042b27f0f54fd59ab..fdf9a8d391cb490ec6b24115c3de49bff92056ac 100644
--- a/doc/workflow/protected_branches.md
+++ b/doc/workflow/protected_branches.md
@@ -1,6 +1,6 @@
 # Protected branches
 
-Permission in GitLab are fundamentally defined around the idea of having read or write permission to the repository and branches.
+Permissions in GitLab are fundamentally defined around the idea of having read or write permission to the repository and branches.
 
 To prevent people from messing with history or pushing code without review, we've created protected branches.
 
diff --git a/doc_styleguide.md b/doc_styleguide.md
index cceb449a8543ac1155a61afaed5b0a0405645937..05ff46323ac721e46b085b9e65d76a8f9083fc27 100644
--- a/doc_styleguide.md
+++ b/doc_styleguide.md
@@ -1,26 +1,3 @@
 # Documentation styleguide
 
-This styleguide recommends best practices to improve documentation and to keep it organized and easy to find. 
-
-## Text
-
-- Split up long lines, this makes it much easier to review and edit. Only
-double line breaks are shown as a full line break in markdown. 80 characters
-is a good line length.
-- For subtitles, make sure to start with the largest and go down, meaning:
-`#` for the title, `##` for subtitles and `###` for subtitles of the subtitles, etc.
-- Make sure that the documentation is added in the correct directory and that there's a link to it somewhere useful.
-- Add only one H1 or title in each document, by adding '#' at the begining of it (when using markdown). 
-For subtitles, use '##', '###' and so on.
-- Do not duplicate information.
-- Be brief and clear.
-- Whenever it applies, add documents in alphabetical order.
-- Write in US English
-- Use [single spaces](http://www.slate.com/articles/technology/technology/2011/01/space_invaders.html) instead of double spaces.
-
-## Images
-
-- Create a directory to store the images with the specific name of the document where the images belong. 
-It could be in the same directory where the .md document that you're working on is located.
-- Images should have a specific, non-generic name that will differentiate them.
-- Keep all file names in lower case.
\ No newline at end of file
+Moved to [development/doc_styleguide](doc/development/doc_styleguide.md).
diff --git a/features/admin/broadcast_messages.feature b/features/admin/broadcast_messages.feature
index b2c3112320ad2bc81c434ccd717250f83e7f2bd8..fd3bac77f867e4c619ad408c81a9174ff9f4722b 100644
--- a/features/admin/broadcast_messages.feature
+++ b/features/admin/broadcast_messages.feature
@@ -2,16 +2,11 @@
 Feature: Admin Broadcast Messages
   Background:
     Given I sign in as an admin
-    And application already has admin messages
+    And application already has a broadcast message
     And I visit admin messages page
 
   Scenario: See broadcast messages list
-    Then I should be all broadcast messages
-
-  Scenario: Create a broadcast message
-    When submit form with new broadcast message
-    Then I should be redirected to admin messages page
-    And I should see newly created broadcast message
+    Then I should see all broadcast messages
 
   Scenario: Create a customized broadcast message
     When submit form with new customized broadcast message
@@ -19,3 +14,14 @@ Feature: Admin Broadcast Messages
     And I should see newly created broadcast message
     Then I visit dashboard page
     And I should see a customized broadcast message
+
+  Scenario: Edit an existing broadcast message
+    When I edit an existing broadcast message
+    And I change the broadcast message text
+    Then I should be redirected to admin messages page
+    And I should see the updated broadcast message
+
+  Scenario: Remove an existing broadcast message
+    When I remove an existing broadcast message
+    Then I should be redirected to admin messages page
+    And I should not see the removed broadcast message
diff --git a/features/project/builds/artifacts.feature b/features/project/builds/artifacts.feature
new file mode 100644
index 0000000000000000000000000000000000000000..1185854453a00b677af178d9a1cd85436ca461ad
--- /dev/null
+++ b/features/project/builds/artifacts.feature
@@ -0,0 +1,53 @@
+Feature: Project Builds Artifacts
+  Background:
+    Given I sign in as a user
+    And I own a project
+    And project has CI enabled
+    And project has a recent build
+
+  Scenario: I download build artifacts
+    Given recent build has artifacts available
+    When I visit recent build details page
+    And I click artifacts download button
+    Then download of build artifacts archive starts
+
+  Scenario: I browse build artifacts
+    Given recent build has artifacts available
+    And recent build has artifacts metadata available
+    When I visit recent build details page
+    And I click artifacts browse button
+    Then I should see content of artifacts archive
+
+  Scenario: I browse subdirectory of build artifacts
+    Given recent build has artifacts available
+    And recent build has artifacts metadata available
+    When I visit recent build details page
+    And I click artifacts browse button
+    And I click link to subdirectory within build artifacts
+    Then I should see content of subdirectory within artifacts archive
+
+  Scenario: I browse directory with UTF-8 characters in name
+    Given recent build has artifacts available
+    And recent build has artifacts metadata available
+    And recent build artifacts contain directory with UTF-8 characters
+    When I visit recent build details page
+    And I click artifacts browse button
+    And I navigate to directory with UTF-8 characters in name
+    Then I should see content of directory with UTF-8 characters in name
+
+  Scenario: I try to browse directory with invalid UTF-8 characters in name
+    Given recent build has artifacts available
+    And recent build has artifacts metadata available
+    And recent build artifacts contain directory with invalid UTF-8 characters
+    When I visit recent build details page
+    And I click artifacts browse button
+    And I navigate to parent directory of directory with invalid name
+    Then I should not see directory with invalid name on the list
+
+  Scenario: I download a single file from build artifacts
+    Given recent build has artifacts available
+    And recent build has artifacts metadata available
+    When I visit recent build details page
+    And I click artifacts browse button
+    And I click a link to file within build artifacts
+    Then download of a file extracted from build artifacts should start
diff --git a/features/project/builds/permissions.feature b/features/project/builds/permissions.feature
new file mode 100644
index 0000000000000000000000000000000000000000..1193bcd74f63a087769c6ab2550b30baf66553f0
--- /dev/null
+++ b/features/project/builds/permissions.feature
@@ -0,0 +1,18 @@
+Feature: Project Builds Permissions
+  Background:
+    Given I sign in as a user
+    And project exists in some group namespace
+    And project has CI enabled
+    And project has a recent build
+
+  Scenario: I try to download build artifacts as guest
+    Given I am member of a project with a guest role
+    And recent build has artifacts available
+    When I access artifacts download page
+    Then page status code should be 404
+
+  Scenario: I try to download build artifacts as reporter
+    Given I am member of a project with a reporter role
+    And recent build has artifacts available
+    When I access artifacts download page
+    Then download of build artifacts archive starts
diff --git a/features/project/builds/summary.feature b/features/project/builds/summary.feature
new file mode 100644
index 0000000000000000000000000000000000000000..b69d279517b7e7cd79f9a0dd8fdf57f9e893a370
--- /dev/null
+++ b/features/project/builds/summary.feature
@@ -0,0 +1,15 @@
+Feature: Project Builds Summary
+  Background:
+    Given I sign in as a user
+    And I own a project
+    And project has CI enabled
+    And project has a recent build
+
+  Scenario: I browse build details page
+    When I visit recent build details page
+    Then I see details of a build
+    And I see build trace
+
+  Scenario: I browse project builds page
+    When I visit project builds page
+    Then I see button to CI Lint
diff --git a/features/project/issues/issues.feature b/features/project/issues/issues.feature
index ab234bc7507dc3766aaa944e2d238c051f35ca89..1502b0952cd681c1f55fd7ecc290f269ccff8551 100644
--- a/features/project/issues/issues.feature
+++ b/features/project/issues/issues.feature
@@ -51,6 +51,14 @@ Feature: Project Issues
     Then I should see comment "XML attached"
     And I should see an error alert section within the comment form
 
+  @javascript
+  Scenario: Visiting Issues after leaving a comment
+    Given I visit issue page "Release 0.4"
+    And I leave a comment like "XML attached"
+    And I visit project "Shop" issues page
+    And I sort the list by "Last updated"
+    Then I should see "Release 0.4" at the top
+
   @javascript
   Scenario: I search issue
     Given I fill in issue search with "Re"
diff --git a/features/project/issues/references.feature b/features/project/issues/references.feature
new file mode 100644
index 0000000000000000000000000000000000000000..4ae2d653337f23c3b8546b720f9170e990e04dc6
--- /dev/null
+++ b/features/project/issues/references.feature
@@ -0,0 +1,33 @@
+@project_issues
+Feature: Project Issues References
+  Background:
+    Given I sign in as "John Doe"
+    And public project "Community"
+    And "John Doe" owns public project "Community"
+    And project "Community" has "Community issue" open issue
+    And I logout
+    And I sign in as "Mary Jane"
+    And private project "Enterprise"
+    And "Mary Jane" owns private project "Enterprise"
+    And project "Enterprise" has "Enterprise issue" open issue
+    And project "Enterprise" has "Enterprise fix" open merge request
+    And I visit issue page "Enterprise issue"
+    And I leave a comment referencing issue "Community issue"
+    And I visit merge request page "Enterprise fix"
+    And I leave a comment referencing issue "Community issue"
+    And I logout
+
+  @javascript
+  Scenario: Viewing the public issue as a "John Doe"
+    Given I sign in as "John Doe"
+    When I visit issue page "Community issue"
+    Then I should not see any related merge requests
+    And I should see no notes at all
+
+  @javascript
+  Scenario: Viewing the public issue as "Mary Jane"
+    Given I sign in as "Mary Jane"
+    When I visit issue page "Community issue"
+    Then I should see the "Enterprise fix" related merge request
+    And I should see a note linking to "Enterprise fix" merge request
+    And I should see a note linking to "Enterprise issue" issue
diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature
index aa9078b878f9d3c39d1b2650d071a501b684e1d8..f1629a26f1014d778d7eeb7911666f436527cd4f 100644
--- a/features/project/merge_requests.feature
+++ b/features/project/merge_requests.feature
@@ -75,6 +75,25 @@ Feature: Project Merge Requests
     And I leave a comment like "XML attached"
     Then I should see comment "XML attached"
 
+  @javascript
+  Scenario: Visiting Merge Requests after leaving a comment
+    Given project "Shop" have "Bug NS-05" open merge request with diffs inside
+    And I visit merge request page "Bug NS-04"
+    And I leave a comment like "XML attached"
+    And I visit project "Shop" merge requests page
+    And I sort the list by "Last updated"
+    Then I should see "Bug NS-04" at the top
+
+  @javascript
+  Scenario: Visiting Merge Requests after commenting on diffs
+    Given project "Shop" have "Bug NS-05" open merge request with diffs inside
+    And I visit merge request page "Bug NS-05"
+    And I click on the Changes tab
+    And I leave a comment like "Line is wrong" on diff
+    And I visit project "Shop" merge requests page
+    And I sort the list by "Last updated"
+    Then I should see "Bug NS-05" at the top
+
   @javascript
   Scenario: I comment on a merge request diff
     Given project "Shop" have "Bug NS-05" open merge request with diffs inside
diff --git a/features/project/merge_requests/references.feature b/features/project/merge_requests/references.feature
new file mode 100644
index 0000000000000000000000000000000000000000..571612261a90531e53af6fa83f6805d79c0266cc
--- /dev/null
+++ b/features/project/merge_requests/references.feature
@@ -0,0 +1,31 @@
+@project_merge_requests
+Feature: Project Merge Requests References
+  Background:
+    Given I sign in as "John Doe"
+    And public project "Community"
+    And "John Doe" owns public project "Community"
+    And project "Community" has "Community fix" open merge request
+    And I logout
+    And I sign in as "Mary Jane"
+    And private project "Enterprise"
+    And "Mary Jane" owns private project "Enterprise"
+    And project "Enterprise" has "Enterprise issue" open issue
+    And project "Enterprise" has "Enterprise fix" open merge request
+    And I visit issue page "Enterprise issue"
+    And I leave a comment referencing issue "Community fix"
+    And I visit merge request page "Enterprise fix"
+    And I leave a comment referencing issue "Community fix"
+    And I logout
+
+  @javascript
+  Scenario: Viewing the public issue as a "John Doe"
+    Given I sign in as "John Doe"
+    When I visit issue page "Community fix"
+    Then I should see no notes at all
+
+  @javascript
+  Scenario: Viewing the public issue as "Mary Jane"
+    Given I sign in as "Mary Jane"
+    When I visit issue page "Community fix"
+    And I should see a note linking to "Enterprise fix" merge request
+    And I should see a note linking to "Enterprise issue" issue
diff --git a/features/project/wiki.feature b/features/project/wiki.feature
index af970ecf2d09d5a40aab6d68e7b9ade87890a270..d4811b1ff54d3a9c080067f72a1c99f54e072ec2 100644
--- a/features/project/wiki.feature
+++ b/features/project/wiki.feature
@@ -69,11 +69,6 @@ Feature: Project Wiki
     And I click on the "Pages" button
     Then I should see non-escaped link in the pages list
 
-  @javascript
-  Scenario: Creating an invalid new page
-    Given I create a New page with an invalid name
-    Then I should see an error message
-
   @javascript
   Scenario: Edit Wiki page that has a path
     Given I create a New page with paths
diff --git a/features/steps/admin/broadcast_messages.rb b/features/steps/admin/broadcast_messages.rb
index f6daf8529775238a8d76c80fa797be26dbf689d4..6cacdf4764c39bbe201822a15fc647392695bae8 100644
--- a/features/steps/admin/broadcast_messages.rb
+++ b/features/steps/admin/broadcast_messages.rb
@@ -1,22 +1,15 @@
 class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
   include SharedAuthentication
   include SharedPaths
-  include SharedAdmin
 
-  step 'application already has admin messages' do
-    FactoryGirl.create(:broadcast_message, message: "Migration to new server")
+  step 'application already has a broadcast message' do
+    FactoryGirl.create(:broadcast_message, :expired, message: "Migration to new server")
   end
 
-  step 'I should be all broadcast messages' do
+  step 'I should see all broadcast messages' do
     expect(page).to have_content "Migration to new server"
   end
 
-  step 'submit form with new broadcast message' do
-    fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
-    select '2018', from: "broadcast_message_ends_at_1i"
-    click_button "Add broadcast message"
-  end
-
   step 'I should be redirected to admin messages page' do
     expect(current_path).to eq admin_broadcast_messages_path
   end
@@ -27,10 +20,9 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
 
   step 'submit form with new customized broadcast message' do
     fill_in 'broadcast_message_message', with: 'Application update from 4:00 CST to 5:00 CST'
-    click_link "Customize colors"
     fill_in 'broadcast_message_color', with: '#f2dede'
     fill_in 'broadcast_message_font', with: '#b94a48'
-    select '2018', from: "broadcast_message_ends_at_1i"
+    select Date.today.next_year.year, from: "broadcast_message_ends_at_1i"
     click_button "Add broadcast message"
   end
 
@@ -38,4 +30,25 @@ class Spinach::Features::AdminBroadcastMessages < Spinach::FeatureSteps
     expect(page).to have_content 'Application update from 4:00 CST to 5:00 CST'
     expect(page).to have_selector %(div[style="background-color: #f2dede; color: #b94a48"])
   end
+
+  step 'I edit an existing broadcast message' do
+    click_link 'Edit'
+  end
+
+  step 'I change the broadcast message text' do
+    fill_in 'broadcast_message_message', with: 'Application update RIGHT NOW'
+    click_button 'Update broadcast message'
+  end
+
+  step 'I should see the updated broadcast message' do
+    expect(page).to have_content "Application update RIGHT NOW"
+  end
+
+  step 'I remove an existing broadcast message' do
+    click_link 'Remove'
+  end
+
+  step 'I should not see the removed broadcast message' do
+    expect(page).not_to have_content 'Migration to new server'
+  end
 end
diff --git a/features/steps/project/builds/artifacts.rb b/features/steps/project/builds/artifacts.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25f2f4e837c7588025471e4a5959279ac02fa36f
--- /dev/null
+++ b/features/steps/project/builds/artifacts.rb
@@ -0,0 +1,76 @@
+class Spinach::Features::ProjectBuildsArtifacts < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedProject
+  include SharedBuilds
+  include RepoHelpers
+
+  step 'I click artifacts download button' do
+    page.within('.artifacts') { click_link 'Download' }
+  end
+
+  step 'I click artifacts browse button' do
+    page.within('.artifacts') { click_link 'Browse' }
+  end
+
+  step 'I should see content of artifacts archive' do
+    page.within('.tree-table') do
+      expect(page).to have_no_content '..'
+      expect(page).to have_content 'other_artifacts_0.1.2'
+      expect(page).to have_content 'ci_artifacts.txt'
+      expect(page).to have_content 'rails_sample.jpg'
+    end
+  end
+
+  step 'I click link to subdirectory within build artifacts' do
+    page.within('.tree-table') { click_link 'other_artifacts_0.1.2' }
+  end
+
+  step 'I should see content of subdirectory within artifacts archive' do
+    page.within('.tree-table') do
+      expect(page).to have_content '..'
+      expect(page).to have_content 'another-subdirectory'
+      expect(page).to have_content 'doc_sample.txt'
+    end
+  end
+
+  step 'recent build artifacts contain directory with UTF-8 characters' do
+    # metadata fixture contains relevant directory
+  end
+
+  step 'I navigate to directory with UTF-8 characters in name' do
+    page.within('.tree-table') { click_link 'tests_encoding' }
+    page.within('.tree-table') { click_link 'utf8 test dir ✓' }
+  end
+
+  step 'I should see content of directory with UTF-8 characters in name' do
+    page.within('.tree-table') do
+      expect(page).to have_content '..'
+      expect(page).to have_content 'regular_file_2'
+    end
+  end
+
+  step 'recent build artifacts contain directory with invalid UTF-8 characters' do
+    # metadata fixture contains relevant directory
+  end
+
+  step 'I navigate to parent directory of directory with invalid name' do
+    page.within('.tree-table') { click_link 'tests_encoding' }
+  end
+
+  step 'I should not see directory with invalid name on the list' do
+    page.within('.tree-table') do
+      expect(page).to have_no_content('non-utf8-dir')
+    end
+  end
+
+  step 'I click a link to file within build artifacts' do
+    page.within('.tree-table') { find_link('ci_artifacts.txt').click }
+  end
+
+  step 'download of a file extracted from build artifacts should start' do
+    # this will be accelerated by Workhorse
+    response_json = JSON.parse(page.body, symbolize_names: true)
+    expect(response_json[:archive]).to end_with('build_artifacts.zip')
+    expect(response_json[:entry]).to eq Base64.encode64('ci_artifacts.txt')
+  end
+end
diff --git a/features/steps/project/builds/permissions.rb b/features/steps/project/builds/permissions.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6e9d6504fd55847b21623b8befea89ac707d5446
--- /dev/null
+++ b/features/steps/project/builds/permissions.rb
@@ -0,0 +1,7 @@
+class Spinach::Features::ProjectBuildsPermissions < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedProject
+  include SharedBuilds
+  include SharedPaths
+  include RepoHelpers
+end
diff --git a/features/steps/project/builds/summary.rb b/features/steps/project/builds/summary.rb
new file mode 100644
index 0000000000000000000000000000000000000000..036bc0a499e5947ebda6e33d894e5af7cbd61d1f
--- /dev/null
+++ b/features/steps/project/builds/summary.rb
@@ -0,0 +1,21 @@
+class Spinach::Features::ProjectBuildsSummary < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedProject
+  include SharedBuilds
+  include RepoHelpers
+
+  step 'I see details of a build' do
+    expect(page).to have_content "Build ##{@build.id}"
+  end
+
+  step 'I see build trace' do
+    expect(page).to have_css '#build-trace'
+  end
+
+  step 'I see button to CI Lint' do
+    page.within('.controls') do
+      ci_lint_tool_link = page.find_link('CI Lint')
+      expect(ci_lint_tool_link[:href]).to eq ci_lint_path
+    end
+  end
+end
diff --git a/features/steps/project/issues/issues.rb b/features/steps/project/issues/issues.rb
index 8e8c9c57452f5218de00570885a90de15efaa855..d556b73f9fdeacb4711281836754f333268484b2 100644
--- a/features/steps/project/issues/issues.rb
+++ b/features/steps/project/issues/issues.rb
@@ -293,6 +293,11 @@ class Spinach::Features::ProjectIssues < Spinach::FeatureSteps
       expect(page).to have_content('Yay!')
     end
   end
+
+  step 'I should see "Release 0.4" at the top' do
+    expect(page.find('ul.content-list.issues-list li.issue:first-child')).to have_content("Release 0.4")
+  end
+
   def filter_issue(text)
     fill_in 'issue_search', with: text
   end
diff --git a/features/steps/project/issues/references.rb b/features/steps/project/issues/references.rb
new file mode 100644
index 0000000000000000000000000000000000000000..69e8b5cbde58f651e53bdcc294baeac06f634563
--- /dev/null
+++ b/features/steps/project/issues/references.rb
@@ -0,0 +1,7 @@
+class Spinach::Features::ProjectIssuesReferences < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedIssuable
+  include SharedNote
+  include SharedProject
+  include SharedUser
+end
diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb
index be993d11093edf42d08122f946436980361575b4..8af635689e0cb59efe1cd346ebcda1f4bfc8e73c 100644
--- a/features/steps/project/merge_requests.rb
+++ b/features/steps/project/merge_requests.rb
@@ -41,7 +41,7 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
   end
 
   step 'I should not see "master" branch' do
-    expect(page).not_to have_content "master"
+    expect(find('.merge-request-info')).not_to have_content "master"
   end
 
   step 'I should see "other_branch" branch' do
@@ -415,6 +415,14 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps
     end
   end
 
+  step 'I should see "Bug NS-05" at the top' do
+    expect(page.find('ul.content-list.mr-list li.merge-request:first-child')).to have_content("Bug NS-05")
+  end
+
+  step 'I should see "Bug NS-04" at the top' do
+    expect(page.find('ul.content-list.mr-list li.merge-request:first-child')).to have_content("Bug NS-04")
+  end
+
   def merge_request
     @merge_request ||= MergeRequest.find_by!(title: "Bug NS-05")
   end
diff --git a/features/steps/project/merge_requests/references.rb b/features/steps/project/merge_requests/references.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ab2ae6847a2dfeaf331a6bd7d69780b59d8f437b
--- /dev/null
+++ b/features/steps/project/merge_requests/references.rb
@@ -0,0 +1,7 @@
+class Spinach::Features::ProjectMergeRequestsReferences < Spinach::FeatureSteps
+  include SharedAuthentication
+  include SharedIssuable
+  include SharedNote
+  include SharedProject
+  include SharedUser
+end
diff --git a/features/steps/project/wiki.rb b/features/steps/project/wiki.rb
index 91d227fadbf6b440851e7a0f767bd091881c9b02..d753ae145900a86c35d57b50c606a34061cfab36 100644
--- a/features/steps/project/wiki.rb
+++ b/features/steps/project/wiki.rb
@@ -132,16 +132,6 @@ class Spinach::Features::ProjectWiki < Spinach::FeatureSteps
     expect(current_path).to include 'one/two/three'
   end
 
-  step 'I create a New page with an invalid name' do
-    click_on 'New Page'
-    fill_in 'Page slug', with: 'invalid name'
-    click_on 'Create Page'
-  end
-
-  step 'I should see an error message' do
-    expect(page).to have_content "The page slug is invalid"
-  end
-
   step 'I should see non-escaped link in the pages list' do
     expect(page).to have_xpath("//a[@href='/#{project.path_with_namespace}/wikis/one/two/three']")
   end
diff --git a/features/steps/shared/active_tab.rb b/features/steps/shared/active_tab.rb
index eb2ccd9d01eb4dfaafe5d28a35272e4e6c209a31..0bee91d758d6859ead46636e5ee4339ac7ef23fa 100644
--- a/features/steps/shared/active_tab.rb
+++ b/features/steps/shared/active_tab.rb
@@ -6,7 +6,7 @@ module SharedActiveTab
   end
 
   def ensure_active_sub_tab(content)
-    expect(find('div.content ul.center-top-menu li.active')).to have_content(content)
+    expect(find('div.content ul.nav-links li.active')).to have_content(content)
   end
 
   def ensure_active_sub_nav(content)
@@ -18,7 +18,7 @@ module SharedActiveTab
   end
 
   step 'no other sub tabs should be active' do
-    expect(page).to have_selector('div.content ul.center-top-menu li.active', count: 1)
+    expect(page).to have_selector('div.content ul.nav-links li.active', count: 1)
   end
 
   step 'no other sub navs should be active' do
diff --git a/features/steps/shared/builds.rb b/features/steps/shared/builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..92bf362879b04c79327e74805fa0ac34a2611c80
--- /dev/null
+++ b/features/steps/shared/builds.rb
@@ -0,0 +1,41 @@
+module SharedBuilds
+  include Spinach::DSL
+
+  step 'project has CI enabled' do
+    @project.enable_ci
+  end
+
+  step 'project has a recent build' do
+    ci_commit = create :ci_commit, project: @project, sha: sample_commit.id
+    @build = create :ci_build, commit: ci_commit
+  end
+
+  step 'I visit recent build details page' do
+    visit namespace_project_build_path(@project.namespace, @project, @build)
+  end
+
+  step 'I visit project builds page' do
+    visit namespace_project_builds_path(@project.namespace, @project)
+  end
+
+  step 'recent build has artifacts available' do
+    artifacts = Rails.root + 'spec/fixtures/ci_build_artifacts.zip'
+    archive = fixture_file_upload(artifacts, 'application/zip')
+    @build.update_attributes(artifacts_file: archive)
+  end
+
+  step 'recent build has artifacts metadata available' do
+    metadata = Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
+    gzip = fixture_file_upload(metadata, 'application/x-gzip')
+    @build.update_attributes(artifacts_metadata: gzip)
+  end
+
+  step 'download of build artifacts archive starts' do
+    expect(page.response_headers['Content-Type']).to eq 'application/zip'
+    expect(page.response_headers['Content-Transfer-Encoding']).to eq 'binary'
+  end
+
+  step 'I access artifacts download page' do
+    visit download_namespace_project_build_artifacts_path(@project.namespace, @project, @build)
+  end
+end
diff --git a/features/steps/shared/issuable.rb b/features/steps/shared/issuable.rb
index e6d1b8b8efc6b238c193097445dc86fd9d4d5d6d..4c5f7488efbd8383572c9bd1c277a33d32a7cc18 100644
--- a/features/steps/shared/issuable.rb
+++ b/features/steps/shared/issuable.rb
@@ -5,6 +5,99 @@ module SharedIssuable
     find(:css, '.issuable-edit').click
   end
 
+  step 'project "Community" has "Community issue" open issue' do
+    create_issuable_for_project(
+      project_name: 'Community',
+      title: 'Community issue'
+    )
+  end
+
+  step 'project "Community" has "Community fix" open merge request' do
+    create_issuable_for_project(
+      project_name: 'Community',
+      type: :merge_request,
+      title: 'Community fix'
+    )
+  end
+
+  step 'project "Enterprise" has "Enterprise issue" open issue' do
+    create_issuable_for_project(
+      project_name: 'Enterprise',
+      title: 'Enterprise issue'
+    )
+  end
+
+  step 'project "Enterprise" has "Enterprise fix" open merge request' do
+    create_issuable_for_project(
+      project_name: 'Enterprise',
+      type: :merge_request,
+      title: 'Enterprise fix'
+    )
+  end
+
+  step 'I leave a comment referencing issue "Community issue"' do
+    leave_reference_comment(
+      issuable: Issue.find_by(title: 'Community issue'),
+      from_project_name: 'Enterprise'
+    )
+  end
+
+  step 'I leave a comment referencing issue "Community fix"' do
+    leave_reference_comment(
+      issuable: MergeRequest.find_by(title: 'Community fix'),
+      from_project_name: 'Enterprise'
+    )
+  end
+
+  step 'I visit issue page "Enterprise issue"' do
+    issue = Issue.find_by(title: 'Enterprise issue')
+    visit namespace_project_issue_path(issue.project.namespace, issue.project, issue)
+  end
+
+  step 'I visit merge request page "Enterprise fix"' do
+    mr = MergeRequest.find_by(title: 'Enterprise fix')
+    visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr)
+  end
+
+  step 'I visit issue page "Community issue"' do
+    issue = Issue.find_by(title: 'Community issue')
+    visit namespace_project_issue_path(issue.project.namespace, issue.project, issue)
+  end
+
+  step 'I visit issue page "Community fix"' do
+    mr = MergeRequest.find_by(title: 'Community fix')
+    visit namespace_project_merge_request_path(mr.target_project.namespace, mr.target_project, mr)
+  end
+
+  step 'I should not see any related merge requests' do
+    page.within '.issue-details' do
+      expect(page).not_to have_content('.merge-requests')
+    end
+  end
+
+  step 'I should see the "Enterprise fix" related merge request' do
+    page.within '.merge-requests' do
+      expect(page).to have_content('1 Related Merge Request')
+      expect(page).to have_content('Enterprise fix')
+    end
+  end
+
+  step 'I should see a note linking to "Enterprise fix" merge request' do
+    visible_note(
+      issuable: MergeRequest.find_by(title: 'Enterprise fix'),
+      from_project_name: 'Community',
+      user_name: 'Mary Jane'
+    )
+  end
+
+  step 'I should see a note linking to "Enterprise issue" issue' do
+    visible_note(
+      issuable: Issue.find_by(title: 'Enterprise issue'),
+      from_project_name: 'Community',
+      user_name: 'Mary Jane'
+    )
+  end
+
   step 'I click link "Edit" for the merge request' do
     edit_issuable
   end
@@ -12,4 +105,45 @@ module SharedIssuable
   step 'I click link "Edit" for the issue' do
     edit_issuable
   end
+
+  def create_issuable_for_project(project_name:, title:, type: :issue)
+    project = Project.find_by(name: project_name)
+
+    attrs = {
+      title: title,
+      author: project.users.first,
+      description: '# Description header'
+    }
+
+    case type
+    when :issue
+      attrs.merge!(project: project)
+    when :merge_request
+      attrs.merge!(
+        source_project: project,
+        target_project: project,
+        source_branch: 'fix',
+        target_branch: 'master'
+      )
+    end
+
+    create(type, attrs)
+  end
+
+  def leave_reference_comment(issuable:, from_project_name:)
+    project = Project.find_by(name: from_project_name)
+
+    page.within('.js-main-target-form') do
+      fill_in 'note[note]', with: "##{issuable.to_reference(project)}"
+      click_button 'Add Comment'
+    end
+  end
+
+  def visible_note(issuable:, from_project_name:, user_name:)
+    project = Project.find_by(name: from_project_name)
+
+    expect(page).to have_content(user_name)
+    expect(page).to have_content("mentioned in #{issuable.class.to_s.titleize.downcase} #{issuable.to_reference(project)}")
+  end
+
 end
diff --git a/features/steps/shared/note.rb b/features/steps/shared/note.rb
index f6aabfefeffb3eab37e11c414feca40785192e1c..eb6df61b8e6b43d6b905ca7d95608fdf12c70119 100644
--- a/features/steps/shared/note.rb
+++ b/features/steps/shared/note.rb
@@ -106,6 +106,10 @@ module SharedNote
     end
   end
 
+  step 'I should see no notes at all' do
+    expect(page).to_not have_css('.note')
+  end
+
   # Markdown
 
   step 'I leave a comment with a header containing "Comment with a header"' do
@@ -140,4 +144,11 @@ module SharedNote
       expect(page).to have_content("+1 Awesome!")
     end
   end
+
+  step 'I sort the list by "Last updated"' do
+    find('button.dropdown-toggle.btn').click
+    page.within('ul.dropdown-menu.dropdown-menu-align-right li') do
+      click_link "Last updated"
+    end
+  end
 end
diff --git a/features/steps/shared/project.rb b/features/steps/shared/project.rb
index da643bf3ba964950a38753fa81d7a333bb1a4af4..d9c75d12238283c569a72a3d68a14434e3e729c4 100644
--- a/features/steps/shared/project.rb
+++ b/features/steps/shared/project.rb
@@ -7,6 +7,11 @@ module SharedProject
     @project.team << [@user, :master]
   end
 
+  step "project exists in some group namespace" do
+    @group = create(:group, name: 'some group')
+    @project = create(:project, namespace: @group)
+  end
+
   # Create a specific project called "Shop"
   step 'I own project "Shop"' do
     @project = Project.find_by(name: "Shop")
@@ -97,6 +102,18 @@ module SharedProject
     @project ||= Project.first
   end
 
+  # ----------------------------------------
+  # Project permissions
+  # ----------------------------------------
+
+  step 'I am member of a project with a guest role' do
+    @project.team << [@user, Gitlab::Access::GUEST]
+  end
+
+  step 'I am member of a project with a reporter role' do
+    @project.team << [@user, Gitlab::Access::REPORTER]
+  end
+
   # ----------------------------------------
   # Visibility of archived project
   # ----------------------------------------
@@ -161,24 +178,33 @@ module SharedProject
   end
 
   step '"John Doe" owns private project "Enterprise"' do
-    user = user_exists("John Doe", username: "john_doe")
-    project = Project.find_by(name: "Enterprise")
-    project ||= create(:empty_project, name: "Enterprise", namespace: user.namespace)
-    project.team << [user, :master]
+    user_owns_project(
+      user_name: 'John Doe',
+      project_name: 'Enterprise'
+    )
+  end
+
+  step '"Mary Jane" owns private project "Enterprise"' do
+    user_owns_project(
+      user_name: 'Mary Jane',
+      project_name: 'Enterprise'
+    )
   end
 
   step '"John Doe" owns internal project "Internal"' do
-    user = user_exists("John Doe", username: "john_doe")
-    project = Project.find_by(name: "Internal")
-    project ||= create :empty_project, :internal, name: 'Internal', namespace: user.namespace
-    project.team << [user, :master]
+    user_owns_project(
+      user_name: 'John Doe',
+      project_name: 'Internal',
+      visibility: :internal
+    )
   end
 
   step '"John Doe" owns public project "Community"' do
-    user = user_exists("John Doe", username: "john_doe")
-    project = Project.find_by(name: "Community")
-    project ||= create :empty_project, :public, name: 'Community', namespace: user.namespace
-    project.team << [user, :master]
+    user_owns_project(
+      user_name: 'John Doe',
+      project_name: 'Community',
+      visibility: :public
+    )
   end
 
   step 'public empty project "Empty Public Project"' do
@@ -213,4 +239,11 @@ module SharedProject
       expect(page).to have_content("skipped")
     end
   end
+
+  def user_owns_project(user_name:, project_name:, visibility: :private)
+    user = user_exists(user_name, username: user_name.gsub(/\s/, '').underscore)
+    project = Project.find_by(name: project_name)
+    project ||= create(:empty_project, visibility, name: project_name, namespace: user.namespace)
+    project.team << [user, :master]
+  end
 end
diff --git a/lib/api/api.rb b/lib/api/api.rb
index 7834262d612244d7162eb33f99e9458b4937200c..7efe0a0262f797ceaa1af95d15d7ad65c18add23 100644
--- a/lib/api/api.rb
+++ b/lib/api/api.rb
@@ -54,5 +54,7 @@ module API
     mount Keys
     mount Tags
     mount Triggers
+    mount Builds
+    mount Variables
   end
 end
diff --git a/lib/api/builds.rb b/lib/api/builds.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d293f988165d8d0d7725290ca74b6b0ee479ec14
--- /dev/null
+++ b/lib/api/builds.rb
@@ -0,0 +1,149 @@
+module API
+  # Projects builds API
+  class Builds < Grape::API
+    before { authenticate! }
+
+    resource :projects do
+      # Get a project builds
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
+      #                      if none provided showing all builds)
+      # Example Request:
+      #   GET /projects/:id/builds
+      get ':id/builds' do
+        builds = user_project.builds.order('id DESC')
+        builds = filter_builds(builds, params[:scope])
+
+        present paginate(builds), with: Entities::Build,
+                                  user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+      end
+
+      # Get builds for a specific commit of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   sha (required) - The SHA id of a commit
+      #   scope (optional) - The scope of builds to show (one or array of: pending, running, failed, success, canceled;
+      #                      if none provided showing all builds)
+      # Example Request:
+      #   GET /projects/:id/repository/commits/:sha/builds
+      get ':id/repository/commits/:sha/builds' do
+        commit = user_project.ci_commits.find_by_sha(params[:sha])
+        return not_found! unless commit
+
+        builds = commit.builds.order('id DESC')
+        builds = filter_builds(builds, params[:scope])
+
+        present paginate(builds), with: Entities::Build,
+                                  user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+      end
+
+      # Get a specific build of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   build_id (required) - The ID of a build
+      # Example Request:
+      #   GET /projects/:id/builds/:build_id
+      get ':id/builds/:build_id' do
+        build = get_build(params[:build_id])
+        return not_found!(build) unless build
+
+        present build, with: Entities::Build,
+                       user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+      end
+
+      # Get a trace of a specific build of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   build_id (required) - The ID of a build
+      # Example Request:
+      #   GET /projects/:id/build/:build_id/trace
+      #
+      # TODO: We should use `present_file!` and leave this implementation for backward compatibility (when build trace
+      #       is saved in the DB instead of file). But before that, we need to consider how to replace the value of
+      #       `runners_token` with some mask (like `xxxxxx`) when sending trace file directly by workhorse.
+      get ':id/builds/:build_id/trace' do
+        build = get_build(params[:build_id])
+        return not_found!(build) unless build
+
+        header 'Content-Disposition', "infile; filename=\"#{build.id}.log\""
+        content_type 'text/plain'
+        env['api.format'] = :binary
+
+        trace = build.trace
+        body trace
+      end
+
+      # Cancel a specific build of a project
+      #
+      # parameters:
+      #   id (required) - the id of a project
+      #   build_id (required) - the id of a build
+      # example request:
+      #   post /projects/:id/build/:build_id/cancel
+      post ':id/builds/:build_id/cancel' do
+        authorize_manage_builds!
+
+        build = get_build(params[:build_id])
+        return not_found!(build) unless build
+
+        build.cancel
+
+        present build, with: Entities::Build,
+                       user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+      end
+
+      # Retry a specific build of a project
+      #
+      # parameters:
+      #   id (required) - the id of a project
+      #   build_id (required) - the id of a build
+      # example request:
+      #   post /projects/:id/build/:build_id/retry
+      post ':id/builds/:build_id/retry' do
+        authorize_manage_builds!
+
+        build = get_build(params[:build_id])
+        return forbidden!('Build is not retryable') unless build && build.retryable?
+
+        build = Ci::Build.retry(build)
+
+        present build, with: Entities::Build,
+                       user_can_download_artifacts: can?(current_user, :download_build_artifacts, user_project)
+      end
+    end
+
+    helpers do
+      def get_build(id)
+        user_project.builds.find_by(id: id.to_i)
+      end
+
+      def filter_builds(builds, scope)
+        return builds if scope.nil? || scope.empty?
+
+        available_statuses = ::CommitStatus::AVAILABLE_STATUSES
+        scope =
+          if scope.is_a?(String)
+            [scope]
+          elsif scope.is_a?(Hashie::Mash)
+            scope.values
+          else
+            ['unknown']
+          end
+
+        unknown = scope - available_statuses
+        render_api_error!('Scope contains invalid value(s)', 400) unless unknown.empty?
+
+        builds.where(status: available_statuses && scope)
+      end
+
+      def authorize_manage_builds!
+        authorize! :manage_builds, user_project
+      end
+    end
+  end
+end
diff --git a/lib/api/entities.rb b/lib/api/entities.rb
index 26e7c956e8f79f2bc23ba4650c9e64693d1e75b0..82a75734de0d3adf2fa5636854f807ab1dafc0d8 100644
--- a/lib/api/entities.rb
+++ b/lib/api/entities.rb
@@ -71,6 +71,7 @@ module API
       expose :avatar_url
       expose :star_count, :forks_count
       expose :open_issues_count, if: lambda { |project, options| project.issues_enabled? && project.default_issues_tracker? }
+      expose :runners_token, if: lambda { |_project, options| options[:user_can_admin_project] }
     end
 
     class ProjectMember < UserBasic
@@ -365,5 +366,40 @@ module API
     class TriggerRequest < Grape::Entity
       expose :id, :variables
     end
+
+    class Runner < Grape::Entity
+      expose :id
+      expose :description
+      expose :active
+      expose :is_shared
+      expose :name
+    end
+
+    class Build < Grape::Entity
+      expose :id, :status, :stage, :name, :ref, :tag, :coverage
+      expose :created_at, :started_at, :finished_at
+      expose :user, with: User
+      # TODO: download_url in Ci:Build model is an GitLab Web Interface URL, not API URL. We should think on some API
+      #       for downloading of artifacts (see: https://gitlab.com/gitlab-org/gitlab-ce/issues/4255)
+      expose :download_url do |repo_obj, options|
+        if options[:user_can_download_artifacts]
+          repo_obj.download_url
+        end
+      end
+      expose :commit, with: RepoCommit do |repo_obj, _options|
+        if repo_obj.respond_to?(:commit)
+          repo_obj.commit.commit_data
+        end
+      end
+      expose :runner, with: Runner
+    end
+
+    class Trigger < Grape::Entity
+      expose :token, :created_at, :updated_at, :deleted_at, :last_used
+    end
+
+    class Variable < Grape::Entity
+      expose :key, :value
+    end
   end
 end
diff --git a/lib/api/helpers.rb b/lib/api/helpers.rb
index a4df810e755ce7c50bdce1316fa1d8a1f8d40724..3f528b9f7c09c2de47ec25c6891ed6794ee7c57c 100644
--- a/lib/api/helpers.rb
+++ b/lib/api/helpers.rb
@@ -97,11 +97,9 @@ module API
     end
 
     def paginate(relation)
-      per_page  = params[:per_page].to_i
-      paginated = relation.page(params[:page]).per(per_page)
-      add_pagination_headers(paginated, per_page)
-
-      paginated
+      relation.page(params[:page]).per(params[:per_page].to_i).tap do |data|
+        add_pagination_headers(data)
+      end
     end
 
     def authenticate!
@@ -266,6 +264,10 @@ module API
         projects = projects.search(params[:search])
       end
 
+      if params[:visibility].present?
+        projects = projects.search_by_visibility(params[:visibility])
+      end
+
       projects.reorder(project_order_by => project_sort)
     end
 
@@ -289,12 +291,14 @@ module API
 
     # file helpers
 
-    def uploaded_file!(field, uploads_path)
+    def uploaded_file(field, uploads_path)
       if params[field]
         bad_request!("#{field} is not a file") unless params[field].respond_to?(:filename)
         return params[field]
       end
 
+      return nil unless params["#{field}.path"] && params["#{field}.name"]
+
       # sanitize file paths
       # this requires all paths to exist
       required_attributes! %W(#{field}.path)
@@ -327,16 +331,26 @@ module API
 
     private
 
-    def add_pagination_headers(paginated, per_page)
+    def add_pagination_headers(paginated_data)
+      header 'X-Total',       paginated_data.total_count.to_s
+      header 'X-Total-Pages', paginated_data.total_pages.to_s
+      header 'X-Per-Page',    paginated_data.limit_value.to_s
+      header 'X-Page',        paginated_data.current_page.to_s
+      header 'X-Next-Page',   paginated_data.next_page.to_s
+      header 'X-Prev-Page',   paginated_data.prev_page.to_s
+      header 'Link',          pagination_links(paginated_data)
+    end
+
+    def pagination_links(paginated_data)
       request_url = request.url.split('?').first
 
       links = []
-      links << %(<#{request_url}?page=#{paginated.current_page - 1}&per_page=#{per_page}>; rel="prev") unless paginated.first_page?
-      links << %(<#{request_url}?page=#{paginated.current_page + 1}&per_page=#{per_page}>; rel="next") unless paginated.last_page?
-      links << %(<#{request_url}?page=1&per_page=#{per_page}>; rel="first")
-      links << %(<#{request_url}?page=#{paginated.total_pages}&per_page=#{per_page}>; rel="last")
+      links << %(<#{request_url}?page=#{paginated_data.current_page - 1}&per_page=#{paginated_data.limit_value}>; rel="prev") unless paginated_data.first_page?
+      links << %(<#{request_url}?page=#{paginated_data.current_page + 1}&per_page=#{paginated_data.limit_value}>; rel="next") unless paginated_data.last_page?
+      links << %(<#{request_url}?page=1&per_page=#{paginated_data.limit_value}>; rel="first")
+      links << %(<#{request_url}?page=#{paginated_data.total_pages}&per_page=#{paginated_data.limit_value}>; rel="last")
 
-      header 'Link', links.join(', ')
+      links.join(', ')
     end
 
     def abilities
diff --git a/lib/api/notes.rb b/lib/api/notes.rb
index 3efdfe2d46e29cdffd59a13d36f8ceb86ae77832..174473f53719925adb4e8c9bd0bb99a5d7f95fc7 100644
--- a/lib/api/notes.rb
+++ b/lib/api/notes.rb
@@ -20,7 +20,19 @@ module API
         #   GET /projects/:id/snippets/:noteable_id/notes
         get ":id/#{noteables_str}/:#{noteable_id_str}/notes" do
           @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
-          present paginate(@noteable.notes), with: Entities::Note
+
+          # We exclude notes that are cross-references and that cannot be viewed
+          # by the current user. By doing this exclusion at this level and not
+          # at the DB query level (which we cannot in that case), the current
+          # page can have less elements than :per_page even if
+          # there's more than one page.
+          notes =
+            # paginate() only works with a relation. This could lead to a
+            # mismatch between the pagination headers info and the actual notes
+            # array returned, but this is really a edge-case.
+            paginate(@noteable.notes).
+            reject { |n| n.cross_reference_not_visible_for?(current_user) }
+          present notes, with: Entities::Note
         end
 
         # Get a single +noteable+ note
@@ -35,7 +47,12 @@ module API
         get ":id/#{noteables_str}/:#{noteable_id_str}/notes/:note_id" do
           @noteable = user_project.send(:"#{noteables_str}").find(params[:"#{noteable_id_str}"])
           @note = @noteable.notes.find(params[:note_id])
-          present @note, with: Entities::Note
+
+          if @note.cross_reference_not_visible_for?(current_user)
+            not_found!("Note")
+          else
+            present @note, with: Entities::Note
+          end
         end
 
         # Create a new +noteable+ note
diff --git a/lib/api/projects.rb b/lib/api/projects.rb
index 8b1390e3289ec8d8db1e55f72b4fd880089cf106..71bb342f8448cacd216acf55fb80a0a56265242b 100644
--- a/lib/api/projects.rb
+++ b/lib/api/projects.rb
@@ -69,7 +69,8 @@ module API
       # Example Request:
       #   GET /projects/:id
       get ":id" do
-        present user_project, with: Entities::ProjectWithAccess, user: current_user
+        present user_project, with: Entities::ProjectWithAccess, user: current_user,
+                              user_can_admin_project: can?(current_user, :admin_project, user_project)
       end
 
       # Get events for a single project
@@ -118,7 +119,8 @@ module API
         attrs = map_public_to_visibility_level(attrs)
         @project = ::Projects::CreateService.new(current_user, attrs).execute
         if @project.saved?
-          present @project, with: Entities::Project
+          present @project, with: Entities::Project,
+                            user_can_admin_project: can?(current_user, :admin_project, @project)
         else
           if @project.errors[:limit_reached].present?
             error!(@project.errors[:limit_reached], 403)
@@ -163,7 +165,8 @@ module API
         attrs = map_public_to_visibility_level(attrs)
         @project = ::Projects::CreateService.new(user, attrs).execute
         if @project.saved?
-          present @project, with: Entities::Project
+          present @project, with: Entities::Project,
+                            user_can_admin_project: can?(current_user, :admin_project, @project)
         else
           render_validation_error!(@project)
         end
@@ -182,8 +185,9 @@ module API
         if @forked_project.errors.any?
           conflict!(@forked_project.errors.messages)
         else
-          present @forked_project, with: Entities::Project
-        end
+          present @forked_project, with: Entities::Project,
+                                   user_can_admin_project: can?(current_user, :admin_project, @forked_project)
+         end
       end
 
       # Update an existing project
@@ -229,7 +233,8 @@ module API
         if user_project.errors.any?
           render_validation_error!(user_project)
         else
-          present user_project, with: Entities::Project
+          present user_project, with: Entities::Project,
+                                user_can_admin_project: can?(current_user, :admin_project, user_project)
         end
       end
 
diff --git a/lib/api/triggers.rb b/lib/api/triggers.rb
index 2781f1cf1917dda336d4c1931483908c3c69560f..5e4964f446cde3a9ab447fac7eec92f26948d190 100644
--- a/lib/api/triggers.rb
+++ b/lib/api/triggers.rb
@@ -43,6 +43,75 @@ module API
           render_api_error!(errors, 400)
         end
       end
+
+      # Get triggers list
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   page (optional) - The page number for pagination
+      #   per_page (optional) - The value of items per page to show
+      # Example Request:
+      #   GET /projects/:id/triggers
+      get ':id/triggers' do
+        authenticate!
+        authorize_admin_project
+
+        triggers = user_project.triggers.includes(:trigger_requests)
+        triggers = paginate(triggers)
+
+        present triggers, with: Entities::Trigger
+      end
+
+      # Get specific trigger of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   token (required) - The `token` of a trigger
+      # Example Request:
+      #   GET /projects/:id/triggers/:token
+      get ':id/triggers/:token' do
+        authenticate!
+        authorize_admin_project
+
+        trigger = user_project.triggers.find_by(token: params[:token].to_s)
+        return not_found!('Trigger') unless trigger
+
+        present trigger, with: Entities::Trigger
+      end
+
+      # Create trigger
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      # Example Request:
+      #   POST /projects/:id/triggers
+      post ':id/triggers' do
+        authenticate!
+        authorize_admin_project
+
+        trigger = user_project.triggers.create
+
+        present trigger, with: Entities::Trigger
+      end
+
+      # Delete trigger
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   token (required) - The `token` of a trigger
+      # Example Request:
+      #   DELETE /projects/:id/triggers/:token
+      delete ':id/triggers/:token' do
+        authenticate!
+        authorize_admin_project
+
+        trigger = user_project.triggers.find_by(token: params[:token].to_s)
+        return not_found!('Trigger') unless trigger
+
+        trigger.destroy
+
+        present trigger, with: Entities::Trigger
+      end
     end
   end
 end
diff --git a/lib/api/users.rb b/lib/api/users.rb
index 0d7813428e29a32727d3479aea909622f5329d52..fd2128bd1795b5e13b8b71f79f5db3f28341f44d 100644
--- a/lib/api/users.rb
+++ b/lib/api/users.rb
@@ -284,10 +284,12 @@ module API
         authenticated_as_admin!
         user = User.find_by(id: params[:id])
 
-        if user
+        if !user
+          not_found!('User')
+        elsif !user.ldap_blocked?
           user.block
         else
-          not_found!('User')
+          forbidden!('LDAP blocked users cannot be modified by the API')
         end
       end
 
@@ -299,10 +301,12 @@ module API
         authenticated_as_admin!
         user = User.find_by(id: params[:id])
 
-        if user
-          user.activate
-        else
+        if !user
           not_found!('User')
+        elsif user.ldap_blocked?
+          forbidden!('LDAP blocked users cannot be unblocked by the API')
+        else
+          user.activate
         end
       end
     end
diff --git a/lib/api/variables.rb b/lib/api/variables.rb
new file mode 100644
index 0000000000000000000000000000000000000000..d9a055f6c927ad00b1e1843d613ff1391fc76ff3
--- /dev/null
+++ b/lib/api/variables.rb
@@ -0,0 +1,95 @@
+module API
+  # Projects variables API
+  class Variables < Grape::API
+    before { authenticate! }
+    before { authorize_admin_project }
+
+    resource :projects do
+      # Get project variables
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   page (optional) - The page number for pagination
+      #   per_page (optional) - The value of items per page to show
+      # Example Request:
+      #   GET /projects/:id/variables
+      get ':id/variables' do
+        variables = user_project.variables
+        present paginate(variables), with: Entities::Variable
+      end
+
+      # Get specific variable of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   key (required) - The `key` of variable
+      # Example Request:
+      #   GET /projects/:id/variables/:key
+      get ':id/variables/:key' do
+        key = params[:key]
+        variable = user_project.variables.find_by(key: key.to_s)
+
+        return not_found!('Variable') unless variable
+
+        present variable, with: Entities::Variable
+      end
+
+      # Create a new variable in project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   key (required) - The key of variable
+      #   value (required) - The value of variable
+      # Example Request:
+      #   POST /projects/:id/variables
+      post ':id/variables' do
+        required_attributes! [:key, :value]
+
+        variable = user_project.variables.create(key: params[:key], value: params[:value])
+
+        if variable.valid?
+          present variable, with: Entities::Variable
+        else
+          render_validation_error!(variable)
+        end
+      end
+
+      # Update existing variable of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   key (optional) - The `key` of variable
+      #   value (optional) - New value for `value` field of variable
+      # Example Request:
+      #   PUT /projects/:id/variables/:key
+      put ':id/variables/:key' do
+        variable = user_project.variables.find_by(key: params[:key].to_s)
+
+        return not_found!('Variable') unless variable
+
+        attrs = attributes_for_keys [:value]
+        if variable.update(attrs)
+          present variable, with: Entities::Variable
+        else
+          render_validation_error!(variable)
+        end
+      end
+
+      # Delete existing variable of a project
+      #
+      # Parameters:
+      #   id (required) - The ID of a project
+      #   key (required) - The ID of a variable
+      # Example Request:
+      #   DELETE /projects/:id/variables/:key
+      delete ':id/variables/:key' do
+        variable = user_project.variables.find_by(key: params[:key].to_s)
+
+        return not_found!('Variable') unless variable
+        variable.destroy
+
+        present variable, with: Entities::Variable
+      end
+    end
+  end
+end
diff --git a/lib/banzai/cross_project_reference.rb b/lib/banzai/cross_project_reference.rb
index ba2866e1efaa2f49222c41a64fd7599afb2ee94a..0257848b6bc1ce8647b8f9af0f57758ee68c91cc 100644
--- a/lib/banzai/cross_project_reference.rb
+++ b/lib/banzai/cross_project_reference.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   # Common methods for ReferenceFilters that support an optional cross-project
   # reference.
diff --git a/lib/banzai/filter.rb b/lib/banzai/filter.rb
index fd4fe024252db0605edda3d7c5e71170801e7030..905c4c0144e2d3670b72cbd7e417531fbe7f8442 100644
--- a/lib/banzai/filter.rb
+++ b/lib/banzai/filter.rb
@@ -1,5 +1,4 @@
 require 'active_support/core_ext/string/output_safety'
-require 'banzai'
 
 module Banzai
   module Filter
diff --git a/lib/banzai/filter/abstract_reference_filter.rb b/lib/banzai/filter/abstract_reference_filter.rb
index b2db10e68641899065d057a83d331176a919d71d..cdbaecf8d9077f3bd7db00b616d2487c1cee73ab 100644
--- a/lib/banzai/filter/abstract_reference_filter.rb
+++ b/lib/banzai/filter/abstract_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # Issues, Merge Requests, Snippets, Commits and Commit Ranges share
diff --git a/lib/banzai/filter/autolink_filter.rb b/lib/banzai/filter/autolink_filter.rb
index da4ee80c1b5cadf5de6ae385251052390b5f8d72..856f56fb1757685e1fd54c88036c2f3718228cbd 100644
--- a/lib/banzai/filter/autolink_filter.rb
+++ b/lib/banzai/filter/autolink_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 require 'uri'
 
diff --git a/lib/banzai/filter/commit_range_reference_filter.rb b/lib/banzai/filter/commit_range_reference_filter.rb
index e67cd45ab9bd1159c6bcd5ea3f6fc762d0ee0a69..470727ee3120a9ca2f5314b148d55a70ff138457 100644
--- a/lib/banzai/filter/commit_range_reference_filter.rb
+++ b/lib/banzai/filter/commit_range_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces commit range references with links.
diff --git a/lib/banzai/filter/commit_reference_filter.rb b/lib/banzai/filter/commit_reference_filter.rb
index 9e57608b483651dec1449ee72d2121cbee59cfb9..713a56ba94940c640d5e3eb802adf236bd0dd0fa 100644
--- a/lib/banzai/filter/commit_reference_filter.rb
+++ b/lib/banzai/filter/commit_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces commit references with links.
diff --git a/lib/banzai/filter/emoji_filter.rb b/lib/banzai/filter/emoji_filter.rb
index 86838e1483c40511a15cdd03c7eb3d3e72dc6e2f..5952a0316269f6ab6e19ada9434b6fed34a203d4 100644
--- a/lib/banzai/filter/emoji_filter.rb
+++ b/lib/banzai/filter/emoji_filter.rb
@@ -1,5 +1,4 @@
 require 'action_controller'
-require 'banzai'
 require 'gitlab_emoji'
 require 'html/pipeline/filter'
 
diff --git a/lib/banzai/filter/external_issue_reference_filter.rb b/lib/banzai/filter/external_issue_reference_filter.rb
index 6136e73c096ab7df26c281a903840b64695e0803..edc26386903aef341b859677ed3cd759f12a500a 100644
--- a/lib/banzai/filter/external_issue_reference_filter.rb
+++ b/lib/banzai/filter/external_issue_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces external issue tracker references with links.
diff --git a/lib/banzai/filter/external_link_filter.rb b/lib/banzai/filter/external_link_filter.rb
index ac87b9820afe7635cd89ed2ffe98782f6e56c727..8d368f3b9e71248e85ca8c68508997e1094250d1 100644
--- a/lib/banzai/filter/external_link_filter.rb
+++ b/lib/banzai/filter/external_link_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/gollum_tags_filter.rb b/lib/banzai/filter/gollum_tags_filter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..fe01dae485075da23a8b05f48bb46ddf3051b90b
--- /dev/null
+++ b/lib/banzai/filter/gollum_tags_filter.rb
@@ -0,0 +1,151 @@
+require 'banzai'
+require 'html/pipeline/filter'
+
+module Banzai
+  module Filter
+    # HTML Filter for parsing Gollum's tags in HTML. It's only parses the
+    # following tags:
+    #
+    # - Link to internal pages:
+    #
+    #   * [[Bug Reports]]
+    #   * [[How to Contribute|Contributing]]
+    #
+    # - Link to external resources:
+    #
+    #   * [[http://en.wikipedia.org/wiki/Git_(software)]]
+    #   * [[Git|http://en.wikipedia.org/wiki/Git_(software)]]
+    #
+    # - Link internal images, the special attributes will be ignored:
+    #
+    #   * [[images/logo.png]]
+    #   * [[images/logo.png|alt=Logo]]
+    #
+    # - Link external images, the special attributes will be ignored:
+    #
+    #   * [[http://example.com/images/logo.png]]
+    #   * [[http://example.com/images/logo.png|alt=Logo]]
+    #
+    # Based on Gollum::Filter::Tags
+    #
+    # Context options:
+    #   :project_wiki (required) - Current project wiki.
+    #
+    class GollumTagsFilter < HTML::Pipeline::Filter
+      include ActionView::Helpers::TagHelper
+
+      # Pattern to match tags content that should be parsed in HTML.
+      #
+      # Gollum's tags have been made to resemble the tags of other markups,
+      # especially MediaWiki. The basic syntax is:
+      #
+      # [[tag]]
+      #
+      # Some tags will accept attributes which are separated by pipe
+      # symbols.Some attributes must precede the tag and some must follow it:
+      #
+      # [[prefix-attribute|tag]]
+      # [[tag|suffix-attribute]]
+      #
+      # See https://github.com/gollum/gollum/wiki
+      #
+      # Rubular: http://rubular.com/r/7dQnE5CUCH
+      TAGS_PATTERN = %r{\[\[(.+?)\]\]}
+
+      # Pattern to match allowed image extensions
+      ALLOWED_IMAGE_EXTENSIONS = %r{.+(jpg|png|gif|svg|bmp)\z}i
+
+      def call
+        search_text_nodes(doc).each do |node|
+          content = node.content
+
+          next unless content.match(TAGS_PATTERN)
+
+          html = process_tag($1)
+
+          if html && html != node.content
+            node.replace(html)
+          end
+        end
+
+        doc
+      end
+
+      private
+
+      # Process a single tag into its final HTML form.
+      #
+      # tag - The String tag contents (the stuff inside the double brackets).
+      #
+      # Returns the String HTML version of the tag.
+      def process_tag(tag)
+        parts = tag.split('|')
+
+        return if parts.size.zero?
+
+        process_image_tag(parts) || process_page_link_tag(parts)
+      end
+
+      # Attempt to process the tag as an image tag.
+      #
+      # tag - The String tag contents (the stuff inside the double brackets).
+      #
+      # Returns the String HTML if the tag is a valid image tag or nil
+      # if it is not.
+      def process_image_tag(parts)
+        content = parts[0].strip
+
+        return unless image?(content)
+
+        if url?(content)
+          path = content
+        elsif file = project_wiki.find_file(content)
+          path = ::File.join project_wiki_base_path, file.path
+        end
+
+        if path
+          content_tag(:img, nil, src: path)
+        end
+      end
+
+      def image?(path)
+        path =~ ALLOWED_IMAGE_EXTENSIONS
+      end
+
+      def url?(path)
+        path.start_with?(*%w(http https))
+      end
+
+      # Attempt to process the tag as a page link tag.
+      #
+      # tag - The String tag contents (the stuff inside the double brackets).
+      #
+      # Returns the String HTML if the tag is a valid page link tag or nil
+      # if it is not.
+      def process_page_link_tag(parts)
+        if parts.size == 1
+          url = parts[0].strip
+        else
+          name, url = *parts.compact.map(&:strip)
+        end
+
+        content_tag(:a, name || url, href: url)
+      end
+
+      def project_wiki
+        context[:project_wiki]
+      end
+
+      def project_wiki_base_path
+        project_wiki && project_wiki.wiki_base_path
+      end
+
+      # Ensure that a :project_wiki key exists in context
+      #
+      # Note that while the key might exist, its value could be nil!
+      def validate
+        needs :project_wiki
+      end
+    end
+  end
+end
diff --git a/lib/banzai/filter/issue_reference_filter.rb b/lib/banzai/filter/issue_reference_filter.rb
index 51180cb901a62f4eeb72853e7f9098e748bf26e4..9f08aa36e8b5bfd9cc541ee08bc65452d2f27212 100644
--- a/lib/banzai/filter/issue_reference_filter.rb
+++ b/lib/banzai/filter/issue_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces issue references with links. References to
diff --git a/lib/banzai/filter/label_reference_filter.rb b/lib/banzai/filter/label_reference_filter.rb
index a3a7a23c1e6ae272f950dcb76154c837d830c3e0..95e7d2091194f8a1e8e14be88387904b33a6f8d3 100644
--- a/lib/banzai/filter/label_reference_filter.rb
+++ b/lib/banzai/filter/label_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces label references with links.
diff --git a/lib/banzai/filter/markdown_filter.rb b/lib/banzai/filter/markdown_filter.rb
index d09cf41df394d478a04f125da729b2f13e03f235..0659fed14190b7ce3e53c9e724c6c8ff712ad9ad 100644
--- a/lib/banzai/filter/markdown_filter.rb
+++ b/lib/banzai/filter/markdown_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/merge_request_reference_filter.rb b/lib/banzai/filter/merge_request_reference_filter.rb
index 755b946a34ba8124d73a25633f9f0913c494b94a..57c717089929b0b21f12249ab8f275f5e6cd1059 100644
--- a/lib/banzai/filter/merge_request_reference_filter.rb
+++ b/lib/banzai/filter/merge_request_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces merge request references with links. References
diff --git a/lib/banzai/filter/redactor_filter.rb b/lib/banzai/filter/redactor_filter.rb
index 66f77902319850ba847defb84a8762941e5b3756..7141ed7c9bd8e5569e7c72055af5bb11188b8970 100644
--- a/lib/banzai/filter/redactor_filter.rb
+++ b/lib/banzai/filter/redactor_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/reference_filter.rb b/lib/banzai/filter/reference_filter.rb
index 7198a8b03e2bfbf5f73e85e31a8915b92fbf596a..3637b1bac94850b6dbd2b28752df881d4a671f18 100644
--- a/lib/banzai/filter/reference_filter.rb
+++ b/lib/banzai/filter/reference_filter.rb
@@ -1,5 +1,4 @@
 require 'active_support/core_ext/string/output_safety'
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
@@ -133,7 +132,8 @@ module Banzai
 
           next unless link && text
 
-          link = URI.decode(link)
+          link = CGI.unescape(link)
+          next unless link.force_encoding('UTF-8').valid_encoding?
           # Ignore ending punctionation like periods or commas
           next unless link == text && text =~ /\A#{pattern}/
 
@@ -170,7 +170,8 @@ module Banzai
           text = node.text
 
           next unless link && text
-          link = URI.decode(link)
+          link = CGI.unescape(link)
+          next unless link.force_encoding('UTF-8').valid_encoding?
           next unless link && link =~ /\A#{pattern}\z/
 
           html = yield link, text
diff --git a/lib/banzai/filter/reference_gatherer_filter.rb b/lib/banzai/filter/reference_gatherer_filter.rb
index bef041129196a2f6aff811369a245329a12dcc5e..86d484feb90831e63e908b77ae6435f0bf7380e6 100644
--- a/lib/banzai/filter/reference_gatherer_filter.rb
+++ b/lib/banzai/filter/reference_gatherer_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/relative_link_filter.rb b/lib/banzai/filter/relative_link_filter.rb
index 66f166939e416ae1be722e1a609a8c9ea80c3150..41380627d397a4d15b8f407b0d5910cad49171dd 100644
--- a/lib/banzai/filter/relative_link_filter.rb
+++ b/lib/banzai/filter/relative_link_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 require 'uri'
 
diff --git a/lib/banzai/filter/sanitization_filter.rb b/lib/banzai/filter/sanitization_filter.rb
index d03e3ae4b3c9996d12823215bb31aa3d30bd5800..3f49d492f2fbf0a5d26a4da452abdd27d100d6c7 100644
--- a/lib/banzai/filter/sanitization_filter.rb
+++ b/lib/banzai/filter/sanitization_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 require 'html/pipeline/sanitization_filter'
 
diff --git a/lib/banzai/filter/snippet_reference_filter.rb b/lib/banzai/filter/snippet_reference_filter.rb
index 1ad5df96f85c8cb7ca222b91f60793084b48059a..c870a42f74153f7ded6ee17b82ea72018b605f5c 100644
--- a/lib/banzai/filter/snippet_reference_filter.rb
+++ b/lib/banzai/filter/snippet_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces snippet references with links. References to
diff --git a/lib/banzai/filter/syntax_highlight_filter.rb b/lib/banzai/filter/syntax_highlight_filter.rb
index c889cc1e97cd3ea62eaffb7715e20da2244ddd9d..8c5855e5ffc5dc211718e28932107ead5ee8216d 100644
--- a/lib/banzai/filter/syntax_highlight_filter.rb
+++ b/lib/banzai/filter/syntax_highlight_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 require 'rouge/plugins/redcarpet'
 
diff --git a/lib/banzai/filter/table_of_contents_filter.rb b/lib/banzai/filter/table_of_contents_filter.rb
index 9b3e67206d5539c49f00d993cb9bdfa9c3ba5cd2..4056dcd6d64b06c4385788b19143fc27ba1f77d7 100644
--- a/lib/banzai/filter/table_of_contents_filter.rb
+++ b/lib/banzai/filter/table_of_contents_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/task_list_filter.rb b/lib/banzai/filter/task_list_filter.rb
index d0ce13003a55a0844c888dc3c52c137c3856f1ed..66608c9859c1087d2008baf4eb2f6660ad2b98b0 100644
--- a/lib/banzai/filter/task_list_filter.rb
+++ b/lib/banzai/filter/task_list_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'task_list/filter'
 
 module Banzai
diff --git a/lib/banzai/filter/upload_link_filter.rb b/lib/banzai/filter/upload_link_filter.rb
index 1a1d0aad8ca6efb35ded2fb9fbb3d292b53833a8..f642aee09678d0cec3702b57d26e18acb92b8cb6 100644
--- a/lib/banzai/filter/upload_link_filter.rb
+++ b/lib/banzai/filter/upload_link_filter.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline/filter'
 require 'uri'
 
diff --git a/lib/banzai/filter/user_reference_filter.rb b/lib/banzai/filter/user_reference_filter.rb
index 964ab60f614b9f3a75ab1039e71a3cd2abf733f9..24f16f8b5479b3964c6ed5e33b9c9f4fc9d1ea25 100644
--- a/lib/banzai/filter/user_reference_filter.rb
+++ b/lib/banzai/filter/user_reference_filter.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Filter
     # HTML filter that replaces user or group references with links.
diff --git a/lib/banzai/lazy_reference.rb b/lib/banzai/lazy_reference.rb
index 073ec5d9801f0ba466662b82a81afdb0f68f6596..1095b4debc76b96c3a2950e42de32958f7d144e7 100644
--- a/lib/banzai/lazy_reference.rb
+++ b/lib/banzai/lazy_reference.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   class LazyReference
     def self.load(refs)
diff --git a/lib/banzai/pipeline.rb b/lib/banzai/pipeline.rb
index 4e017809d9d3c85d578f1173182485986aee21c2..142a9962eb193bfdac324d1317ffca4fb843df5a 100644
--- a/lib/banzai/pipeline.rb
+++ b/lib/banzai/pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     def self.[](name)
diff --git a/lib/banzai/pipeline/asciidoc_pipeline.rb b/lib/banzai/pipeline/asciidoc_pipeline.rb
index 5e76a817be512e619a63bff9c70adbe717306d7f..f1331c0ebf94567948a2474b604c2db16e834d3f 100644
--- a/lib/banzai/pipeline/asciidoc_pipeline.rb
+++ b/lib/banzai/pipeline/asciidoc_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class AsciidocPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/atom_pipeline.rb b/lib/banzai/pipeline/atom_pipeline.rb
index 957f352aec5f2d149e89db0b09ee041b0b5da759..9694e4bc23f0c2181494d78cc6899bbff37292a5 100644
--- a/lib/banzai/pipeline/atom_pipeline.rb
+++ b/lib/banzai/pipeline/atom_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class AtomPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/base_pipeline.rb b/lib/banzai/pipeline/base_pipeline.rb
index cd30009e5c097e0cc5045750e44f9dc9eef59865..db5177db7b367949dd9220f3a6b345d8bda30dea 100644
--- a/lib/banzai/pipeline/base_pipeline.rb
+++ b/lib/banzai/pipeline/base_pipeline.rb
@@ -1,4 +1,3 @@
-require 'banzai'
 require 'html/pipeline'
 
 module Banzai
diff --git a/lib/banzai/pipeline/combined_pipeline.rb b/lib/banzai/pipeline/combined_pipeline.rb
index f3bf1809d18dc431be986ffe795a2ebfc46679d0..9485199132ecd83bdfb961164a5cb6eb0b2c4f1c 100644
--- a/lib/banzai/pipeline/combined_pipeline.rb
+++ b/lib/banzai/pipeline/combined_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     module CombinedPipeline
diff --git a/lib/banzai/pipeline/description_pipeline.rb b/lib/banzai/pipeline/description_pipeline.rb
index 94c2cb165a5646d021e5b9b829e7e6b843954d2c..20e24ace3525c1c4460b24f3fef604bb4f8c090f 100644
--- a/lib/banzai/pipeline/description_pipeline.rb
+++ b/lib/banzai/pipeline/description_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class DescriptionPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/email_pipeline.rb b/lib/banzai/pipeline/email_pipeline.rb
index 14356145a357791ebca63db494a4e8fdde183c46..e47c384afc11ce30fdbae42d10df6f3d7eaa1414 100644
--- a/lib/banzai/pipeline/email_pipeline.rb
+++ b/lib/banzai/pipeline/email_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class EmailPipeline < FullPipeline
diff --git a/lib/banzai/pipeline/full_pipeline.rb b/lib/banzai/pipeline/full_pipeline.rb
index 72395a5d50ef223860877c76706f73a912cbdef1..d47ddfda4be60cd4a163b77b8bee191697974ec8 100644
--- a/lib/banzai/pipeline/full_pipeline.rb
+++ b/lib/banzai/pipeline/full_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class FullPipeline < CombinedPipeline.new(PlainMarkdownPipeline, GfmPipeline)
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 838155e883190eb77635a4cb74eb982bd3a2c9d7..b7a38ea8427db200621e42bf76fa182938cc275e 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class GfmPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/note_pipeline.rb b/lib/banzai/pipeline/note_pipeline.rb
index 893351438525c4ec7de8c894e428d134e92f9bcd..7890f20f71663a5b2f10b709c9efcd7982d4a46e 100644
--- a/lib/banzai/pipeline/note_pipeline.rb
+++ b/lib/banzai/pipeline/note_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class NotePipeline < FullPipeline
diff --git a/lib/banzai/pipeline/plain_markdown_pipeline.rb b/lib/banzai/pipeline/plain_markdown_pipeline.rb
index 998fd75daa2d57b1efbd9b8d6cfa78e3bc10f7bd..3fbc681457ba86481f62301b162f45f6becd4d11 100644
--- a/lib/banzai/pipeline/plain_markdown_pipeline.rb
+++ b/lib/banzai/pipeline/plain_markdown_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class PlainMarkdownPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/post_process_pipeline.rb b/lib/banzai/pipeline/post_process_pipeline.rb
index 148f24b6ce1d0c29259f13b4bb6f0609e7200e8f..bd338c045f3628be91894b93b0f753d84282a4e8 100644
--- a/lib/banzai/pipeline/post_process_pipeline.rb
+++ b/lib/banzai/pipeline/post_process_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class PostProcessPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/reference_extraction_pipeline.rb b/lib/banzai/pipeline/reference_extraction_pipeline.rb
index 4f9bc9fcccc501fb5d3386358fe13934047c331c..eaddccd30a5e53517f96b798bd193738a990b92a 100644
--- a/lib/banzai/pipeline/reference_extraction_pipeline.rb
+++ b/lib/banzai/pipeline/reference_extraction_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class ReferenceExtractionPipeline < BasePipeline
diff --git a/lib/banzai/pipeline/single_line_pipeline.rb b/lib/banzai/pipeline/single_line_pipeline.rb
index a3c9d4f43aa0964b8b6a4e91426a8d6ad48556dd..8b84ab401df1e585807d538fde837a1a3ffe978b 100644
--- a/lib/banzai/pipeline/single_line_pipeline.rb
+++ b/lib/banzai/pipeline/single_line_pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   module Pipeline
     class SingleLinePipeline < GfmPipeline
diff --git a/lib/banzai/pipeline/wiki_pipeline.rb b/lib/banzai/pipeline/wiki_pipeline.rb
new file mode 100644
index 0000000000000000000000000000000000000000..50b5450e70bd46f02da959fe09cab7665031de59
--- /dev/null
+++ b/lib/banzai/pipeline/wiki_pipeline.rb
@@ -0,0 +1,11 @@
+require 'banzai'
+
+module Banzai
+  module Pipeline
+    class WikiPipeline < FullPipeline
+      def self.filters
+        super.insert(1, Filter::GollumTagsFilter)
+      end
+    end
+  end
+end
diff --git a/lib/banzai/reference_extractor.rb b/lib/banzai/reference_extractor.rb
index 2c197d31898cb39b21f695de15c4d6daee91a6a0..f4079538ec55d9ea88752607318e7767b6a6c233 100644
--- a/lib/banzai/reference_extractor.rb
+++ b/lib/banzai/reference_extractor.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Banzai
   # Extract possible GFM references from an arbitrary String for further processing.
   class ReferenceExtractor
diff --git a/lib/ci/api/builds.rb b/lib/ci/api/builds.rb
index 15faa6edd8402c2ca4a11a8c7493db453153f1b3..690bbf97a89a546f8885c153396f300c297faafd 100644
--- a/lib/ci/api/builds.rb
+++ b/lib/ci/api/builds.rb
@@ -20,7 +20,7 @@ module Ci
 
           if build
             update_runner_info
-            present build, with: Entities::Build
+            present build, with: Entities::BuildDetails
           else
             not_found!
           end
@@ -78,11 +78,13 @@ module Ci
         # Parameters:
         #   id (required) - The ID of a build
         #   token (required) - The build authorization token
-        #   file (required) - The uploaded file
+        #   file (required) - Artifacts file
         # Parameters (accelerated by GitLab Workhorse):
         #   file.path - path to locally stored body (generated by Workhorse)
         #   file.name - real filename as send in Content-Disposition
         #   file.type - real content type as send in Content-Type
+        #   metadata.path - path to locally stored body (generated by Workhorse)
+        #   metadata.name - filename (generated by Workhorse)
         # Headers:
         #   BUILD-TOKEN (required) - The build authorization token, the same as token
         # Body:
@@ -96,13 +98,20 @@ module Ci
           build = Ci::Build.find_by_id(params[:id])
           not_found! unless build
           authenticate_build_token!(build)
-          forbidden!('build is not running') unless build.running?
+          forbidden!('Build is not running!') unless build.running?
+
+          artifacts_upload_path = ArtifactUploader.artifacts_upload_path
+          artifacts = uploaded_file(:file, artifacts_upload_path)
+          metadata = uploaded_file(:metadata, artifacts_upload_path)
+
+          bad_request!('Missing artifacts file!') unless artifacts
+          file_to_large! unless artifacts.size < max_artifacts_size
 
-          file = uploaded_file!(:file, ArtifactUploader.artifacts_upload_path)
-          file_to_large! unless file.size < max_artifacts_size
+          build.artifacts_file = artifacts
+          build.artifacts_metadata = metadata
 
-          if build.update_attributes(artifacts_file: file)
-            present build, with: Entities::Build
+          if build.save
+            present(build, with: Entities::BuildDetails)
           else
             render_validation_error!(build)
           end
@@ -148,6 +157,7 @@ module Ci
           not_found! unless build
           authenticate_build_token!(build)
           build.remove_artifacts_file!
+          build.remove_artifacts_metadata!
         end
       end
     end
diff --git a/lib/ci/api/entities.rb b/lib/ci/api/entities.rb
index e4ac0545ea2f5f707f07adfe2f14ecb0eea5f6d8..b25e0e573a8bd215402407aec01f7dcdf0013fbe 100644
--- a/lib/ci/api/entities.rb
+++ b/lib/ci/api/entities.rb
@@ -16,10 +16,19 @@ module Ci
       end
 
       class Build < Grape::Entity
-        expose :id, :commands, :ref, :sha, :status, :project_id, :repo_url,
-          :before_sha, :allow_git_fetch, :project_name
-
+        expose :id, :ref, :tag, :sha, :status
         expose :name, :token, :stage
+        expose :project_id
+        expose :project_name
+        expose :artifacts_file, using: ArtifactFile, if: lambda { |build, opts| build.artifacts? }
+      end
+
+      class BuildDetails < Build
+        expose :commands
+        expose :repo_url
+        expose :before_sha
+        expose :allow_git_fetch
+        expose :token
 
         expose :options do |model|
           model.options
@@ -30,7 +39,7 @@ module Ci
         end
 
         expose :variables
-        expose :artifacts_file, using: ArtifactFile
+        expose :depends_on_builds, using: Build
       end
 
       class Runner < Grape::Entity
diff --git a/lib/ci/gitlab_ci_yaml_processor.rb b/lib/ci/gitlab_ci_yaml_processor.rb
index bcdfd38d292f245b4fe4df8ed7dc93fb4a67dc57..1a3f662811a0a12467cd8cffe520b38bf64a842d 100644
--- a/lib/ci/gitlab_ci_yaml_processor.rb
+++ b/lib/ci/gitlab_ci_yaml_processor.rb
@@ -115,6 +115,10 @@ module Ci
       end
 
       if @cache
+        if @cache[:key] && !validate_string(@cache[:key])
+          raise ValidationError, "cache:key parameter should be a string"
+        end
+
         if @cache[:untracked] && !validate_boolean(@cache[:untracked])
           raise ValidationError, "cache:untracked parameter should be an boolean"
         end
@@ -198,6 +202,10 @@ module Ci
     end
 
     def validate_job_cache!(name, job)
+      if job[:cache][:key] && !validate_string(job[:cache][:key])
+        raise ValidationError, "#{name} job: cache:key parameter should be a string"
+      end
+
       if job[:cache][:untracked] && !validate_boolean(job[:cache][:untracked])
         raise ValidationError, "#{name} job: cache:untracked parameter should be an boolean"
       end
diff --git a/lib/dnsxl_check.rb b/lib/dnsxl_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1e506b2d9cbcc334bcec9ae4a12d28e5063a377f
--- /dev/null
+++ b/lib/dnsxl_check.rb
@@ -0,0 +1,105 @@
+require 'resolv'
+
+class DNSXLCheck
+
+  class Resolver
+    def self.search(query)
+      begin
+        Resolv.getaddress(query)
+        true
+      rescue Resolv::ResolvError
+        false
+      end
+    end
+  end
+
+  IP_REGEXP = /\A(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\z/
+  DEFAULT_THRESHOLD = 0.33
+
+  def self.create_from_list(list)
+    dnsxl_check = DNSXLCheck.new
+
+    list.each do |entry|
+      dnsxl_check.add_list(entry.domain, entry.weight)
+    end
+
+    dnsxl_check
+  end
+
+  def test(ip)
+    if use_threshold?
+      test_with_threshold(ip)
+    else
+      test_strict(ip)
+    end
+  end
+
+  def test_with_threshold(ip)
+    return false if lists.empty?
+
+    search(ip)
+    final_score >= threshold
+  end
+
+  def test_strict(ip)
+    return false if lists.empty?
+
+    search(ip)
+    @score > 0
+  end
+
+  def use_threshold=(value)
+    @use_threshold = value == true
+  end
+
+  def use_threshold?
+    @use_threshold &&= true
+  end
+
+  def threshold=(threshold)
+    raise ArgumentError, "'threshold' value must be grather than 0 and less than or equal to 1" unless threshold > 0 && threshold <= 1
+    @threshold = threshold
+  end
+
+  def threshold
+    @threshold ||= DEFAULT_THRESHOLD
+  end
+
+  def add_list(domain, weight)
+    @lists ||= []
+    @lists << { domain: domain, weight: weight }
+  end
+
+  def lists
+    @lists ||= []
+  end
+
+  private
+
+  def search(ip)
+    raise ArgumentError, "'ip' value must be in #{IP_REGEXP} format" unless ip.match(IP_REGEXP)
+
+    @score = 0
+
+    reversed = reverse_ip(ip)
+    search_in_rbls(reversed)
+  end
+
+  def reverse_ip(ip)
+    ip.split('.').reverse.join('.')
+  end
+
+  def search_in_rbls(reversed_ip)
+    lists.each do |rbl|
+      query = "#{reversed_ip}.#{rbl[:domain]}"
+      @score += rbl[:weight] if Resolver.search(query)
+    end
+  end
+
+  def final_score
+    weights = lists.map{ |rbl| rbl[:weight] }.reduce(:+).to_i
+    return 0 if weights == 0
+
+    (@score.to_f / weights.to_f).round(2)
+  end
+end
diff --git a/lib/gitlab/ci/build/artifacts/metadata.rb b/lib/gitlab/ci/build/artifacts/metadata.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1344f5d120b32f6f17d84dda408ddebb04e00cea
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/metadata.rb
@@ -0,0 +1,109 @@
+require 'zlib'
+require 'json'
+
+module Gitlab
+  module Ci
+    module Build
+      module Artifacts
+        class Metadata
+          class ParserError < StandardError; end
+
+          VERSION_PATTERN = /^[\w\s]+(\d+\.\d+\.\d+)/
+          INVALID_PATH_PATTERN = %r{(^\.?\.?/)|(/\.?\.?/)}
+
+          attr_reader :file, :path, :full_version
+
+          def initialize(file, path)
+            @file, @path = file, path
+            @full_version = read_version
+          end
+
+          def version
+            @full_version.match(VERSION_PATTERN)[1]
+          end
+
+          def errors
+            gzip do |gz|
+              read_string(gz) # version
+              errors = read_string(gz)
+              raise ParserError, 'Errors field not found!' unless errors
+
+              begin
+                JSON.parse(errors)
+              rescue JSON::ParserError
+                raise ParserError, 'Invalid errors field!'
+              end
+            end
+          end
+
+          def find_entries!
+            gzip do |gz|
+              2.times { read_string(gz) } # version and errors fields
+              match_entries(gz)
+            end
+          end
+
+          def to_entry
+            entries = find_entries!
+            Entry.new(@path, entries)
+          end
+
+          private
+
+          def match_entries(gz)
+            entries = {}
+            match_pattern = %r{^#{Regexp.escape(@path)}[^/]*/?$}
+
+            until gz.eof? do
+              begin
+                path = read_string(gz).force_encoding('UTF-8')
+                meta = read_string(gz).force_encoding('UTF-8')
+               
+                next unless path.valid_encoding? && meta.valid_encoding?
+                next unless path =~ match_pattern
+                next if path =~ INVALID_PATH_PATTERN
+
+                entries[path] = JSON.parse(meta, symbolize_names: true)
+              rescue JSON::ParserError, Encoding::CompatibilityError
+                next
+              end
+            end
+
+            entries
+          end
+
+          def read_version
+            gzip do |gz|
+              version_string = read_string(gz)
+
+              unless version_string
+                raise ParserError, 'Artifacts metadata file empty!'
+              end
+
+              unless version_string =~ VERSION_PATTERN
+                raise ParserError, 'Invalid version!'
+              end
+
+              version_string.chomp
+            end
+          end
+
+          def read_uint32(gz)
+            binary = gz.read(4)
+            binary.unpack('L>')[0] if binary
+          end
+
+          def read_string(gz)
+            string_size = read_uint32(gz)
+            return nil unless string_size
+            gz.read(string_size)
+          end
+
+          def gzip(&block)
+            Zlib::GzipReader.open(@file, &block)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ci/build/artifacts/metadata/entry.rb b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
new file mode 100644
index 0000000000000000000000000000000000000000..25b71fc3275e00cfd12c60d4bceae22d84e2303b
--- /dev/null
+++ b/lib/gitlab/ci/build/artifacts/metadata/entry.rb
@@ -0,0 +1,119 @@
+module Gitlab
+  module Ci::Build::Artifacts
+    class Metadata
+      ##
+      # Class that represents an entry (path and metadata) to a file or
+      # directory in GitLab CI Build Artifacts binary file / archive
+      #
+      # This is IO-operations safe class, that does similar job to
+      # Ruby's Pathname but without the risk of accessing filesystem.
+      #
+      # This class is working only with UTF-8 encoded paths.
+      #
+      class Entry
+        attr_reader :path, :entries
+        attr_accessor :name
+
+        def initialize(path, entries)
+          @path = path.dup.force_encoding('UTF-8')
+          @entries = entries
+
+          if path.include?("\0")
+            raise ArgumentError, 'Path contains zero byte character!'
+          end
+
+          unless path.valid_encoding?
+            raise ArgumentError, 'Path contains non-UTF-8 byte sequence!'
+          end
+        end
+
+        def directory?
+          blank_node? || @path.end_with?('/')
+        end
+
+        def file?
+          !directory?
+        end
+
+        def has_parent?
+          nodes > 0
+        end
+
+        def parent
+          return nil unless has_parent?
+          self.class.new(@path.chomp(basename), @entries)
+        end
+
+        def basename
+          (directory? && !blank_node?) ? name + '/' : name
+        end
+
+        def name
+          @name || @path.split('/').last.to_s
+        end
+
+        def children
+          return [] unless directory?
+          return @children if @children
+
+          child_pattern = %r{^#{Regexp.escape(@path)}[^/]+/?$}
+          @children = select_entries { |path| path =~ child_pattern }
+        end
+
+        def directories(opts = {})
+          return [] unless directory?
+          dirs = children.select(&:directory?)
+          return dirs unless has_parent? && opts[:parent]
+
+          dotted_parent = parent
+          dotted_parent.name = '..'
+          dirs.prepend(dotted_parent)
+        end
+
+        def files
+          return [] unless directory?
+          children.select(&:file?)
+        end
+
+        def metadata
+          @entries[@path] || {}
+        end
+
+        def nodes
+          @path.count('/') + (file? ? 1 : 0)
+        end
+
+        def blank_node?
+          @path.empty? # "" is considered to be './'
+        end
+
+        def exists?
+          blank_node? || @entries.include?(@path)
+        end
+
+        def empty?
+          children.empty?
+        end
+
+        def to_s
+          @path
+        end
+
+        def ==(other)
+          @path == other.path && @entries == other.entries
+        end
+
+        def inspect
+          "#{self.class.name}: #{@path}"
+        end
+
+        private
+
+        def select_entries
+          selected = @entries.select { |path, _metadata| yield path }
+          selected.map { |path, _metadata| self.class.new(path, @entries) }
+        end
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/current_settings.rb b/lib/gitlab/current_settings.rb
index 7f938780ab11411b30f598326a2d5f4994f6a6a9..ea054255820a8188134f60db94553a4336b815d3 100644
--- a/lib/gitlab/current_settings.rb
+++ b/lib/gitlab/current_settings.rb
@@ -39,7 +39,6 @@ module Gitlab
                end
 
       use_db && ActiveRecord::Base.connection.active? &&
-                !ActiveRecord::Migrator.needs_migration? &&
                 ActiveRecord::Base.connection.table_exists?('application_settings')
 
     rescue ActiveRecord::NoDatabaseError
diff --git a/lib/gitlab/diff/file.rb b/lib/gitlab/diff/file.rb
index 79061cd014181d0375b622431ae41c5379f493c6..a484177ae33f4c05a2abd1418211718f76654134 100644
--- a/lib/gitlab/diff/file.rb
+++ b/lib/gitlab/diff/file.rb
@@ -1,13 +1,22 @@
 module Gitlab
   module Diff
     class File
-      attr_reader :diff
+      attr_reader :diff, :diff_refs
 
       delegate :new_file, :deleted_file, :renamed_file,
         :old_path, :new_path, to: :diff, prefix: false
 
-      def initialize(diff)
+      def initialize(diff, diff_refs)
         @diff = diff
+        @diff_refs = diff_refs
+      end
+
+      def old_ref
+        diff_refs[0] if diff_refs
+      end
+
+      def new_ref
+        diff_refs[1] if diff_refs
       end
 
       # Array of Gitlab::DIff::Line objects
@@ -15,6 +24,14 @@ module Gitlab
         @lines ||= parser.parse(raw_diff.lines)
       end
 
+      def highlighted_diff_lines
+        Gitlab::Diff::Highlight.new(self).highlight
+      end
+
+      def parallel_diff_lines
+        Gitlab::Diff::ParallelDiff.new(self).parallelize
+      end
+
       def mode_changed?
         !!(diff.a_mode && diff.b_mode && diff.a_mode != diff.b_mode)
       end
diff --git a/lib/gitlab/diff/highlight.rb b/lib/gitlab/diff/highlight.rb
new file mode 100644
index 0000000000000000000000000000000000000000..179f8164c84957fda4f0847c12cf7dde055b834f
--- /dev/null
+++ b/lib/gitlab/diff/highlight.rb
@@ -0,0 +1,75 @@
+module Gitlab
+  module Diff
+    class Highlight
+      attr_reader :diff_file
+
+      delegate :old_path, :new_path, :old_ref, :new_ref, to: :diff_file, prefix: :diff
+
+      def initialize(diff_file)
+        @diff_file = diff_file
+        @diff_lines = diff_file.diff_lines
+        @raw_lines = @diff_lines.map(&:text)
+      end
+
+      def highlight
+        @diff_lines.each_with_index do |diff_line, i|
+          # ignore highlighting for "match" lines
+          next if diff_line.type == 'match' || diff_line.type == 'nonewline'
+
+          rich_line = highlight_line(diff_line, i)
+
+          if line_inline_diffs = inline_diffs[i]
+            rich_line = InlineDiffMarker.new(diff_line.text, rich_line).mark(line_inline_diffs)
+          end
+
+          diff_line.text = rich_line.html_safe
+        end
+
+        @diff_lines
+      end
+
+      private
+
+      def highlight_line(diff_line, index)
+        return html_escape(diff_line.text) unless diff_file.diff_refs
+
+        line_prefix = diff_line.text.match(/\A(.)/) ? $1 : ' '
+
+        case diff_line.type
+        when 'new', nil
+          rich_line = new_lines[diff_line.new_pos - 1]
+        when 'old'
+          rich_line = old_lines[diff_line.old_pos - 1]
+        end
+
+        # Only update text if line is found. This will prevent
+        # issues with submodules given the line only exists in diff content.
+        rich_line ? line_prefix + rich_line : html_escape(diff_line.text)
+      end
+
+      def inline_diffs
+        @inline_diffs ||= InlineDiff.new(@raw_lines).inline_diffs
+      end
+
+      def old_lines
+        @old_lines ||= Gitlab::Highlight.highlight_lines(*processing_args(:old))
+      end
+
+      def new_lines
+        @new_lines ||= Gitlab::Highlight.highlight_lines(*processing_args(:new))
+      end
+
+      def processing_args(version)
+        ref  = send("diff_#{version}_ref")
+        path = send("diff_#{version}_path")
+
+        [ref.project.repository, ref.id, path]
+      end
+
+      def html_escape(str)
+        replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
+        str.gsub(/[&"'><]/, replacements)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/diff/inline_diff.rb b/lib/gitlab/diff/inline_diff.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b8a61ad6115a7f830525e612ab3057cbe3ca8d2f
--- /dev/null
+++ b/lib/gitlab/diff/inline_diff.rb
@@ -0,0 +1,73 @@
+module Gitlab
+  module Diff
+    class InlineDiff
+      attr_accessor :lines
+
+      def initialize(lines)
+        @lines = lines
+      end
+
+      def inline_diffs
+        inline_diffs = []
+
+        local_edit_indexes.each do |index|
+          old_index = index
+          new_index = index + 1
+          old_line = @lines[old_index]
+          new_line = @lines[new_index]
+
+          # Skip inline diff if empty line was replaced with content
+          next if old_line[1..-1] == ""
+
+          # Add one, because this is based on the prefixless version
+          lcp = longest_common_prefix(old_line[1..-1], new_line[1..-1]) + 1
+          lcs = longest_common_suffix(old_line[lcp..-1], new_line[lcp..-1])
+
+          old_diff_range = lcp..(old_line.length - lcs - 1)
+          new_diff_range = lcp..(new_line.length - lcs - 1)
+
+          inline_diffs[old_index] = [old_diff_range] if old_diff_range.begin <= old_diff_range.end
+          inline_diffs[new_index] = [new_diff_range] if new_diff_range.begin <= new_diff_range.end
+        end
+
+        inline_diffs
+      end
+
+      private
+
+      # Find runs of single line edits
+      def local_edit_indexes
+        line_prefixes = @lines.map { |line| line.match(/\A([+-])/) ? $1 : ' ' }
+        joined_line_prefixes = " #{line_prefixes.join} "
+
+        offset = 0
+        local_edit_indexes = []
+        while index = joined_line_prefixes.index(" -+ ", offset)
+          local_edit_indexes << index
+          offset = index + 1
+        end
+
+        local_edit_indexes
+      end
+
+      def longest_common_prefix(a, b)
+        max_length = [a.length, b.length].max
+
+        length = 0
+        (0..max_length - 1).each do |pos|
+          old_char = a[pos]
+          new_char = b[pos]
+
+          break if old_char != new_char
+          length += 1
+        end
+
+        length
+      end
+
+      def longest_common_suffix(a, b)
+        longest_common_prefix(a.reverse, b.reverse)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/diff/inline_diff_marker.rb b/lib/gitlab/diff/inline_diff_marker.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1d7fa1bce061ec90fea98bf014bb6b185d97cc54
--- /dev/null
+++ b/lib/gitlab/diff/inline_diff_marker.rb
@@ -0,0 +1,109 @@
+module Gitlab
+  module Diff
+    class InlineDiffMarker
+      attr_accessor :raw_line, :rich_line
+
+      def initialize(raw_line, rich_line = raw_line)
+        @raw_line = raw_line
+        @rich_line = rich_line
+      end
+
+      def mark(line_inline_diffs)
+        marker_ranges = []
+        line_inline_diffs.each do |inline_diff_range|
+          # Map the inline-diff range based on the raw line to character positions in the rich line
+          inline_diff_positions = position_mapping[inline_diff_range].flatten
+          # Turn the array of character positions into ranges
+          marker_ranges.concat(collapse_ranges(inline_diff_positions))
+        end
+
+        offset = 0
+        # Mark each range
+        marker_ranges.each do |range|
+          offset = insert_around_range(rich_line, range, "<span class='idiff'>", "</span>", offset)
+        end
+
+        rich_line
+      end
+
+      private
+
+      # Mapping of character positions in the raw line, to the rich (highlighted) line
+      def position_mapping
+        @position_mapping ||= begin
+          mapping = []
+          rich_pos = 0
+          (0..raw_line.length).each do |raw_pos|
+            rich_char = rich_line[rich_pos]
+
+            # The raw and rich lines are the same except for HTML tags,
+            # so skip over any `<...>` segment
+            while rich_char == '<'
+              until rich_char == '>'
+                rich_pos += 1
+                rich_char = rich_line[rich_pos]
+              end
+
+              rich_pos += 1
+              rich_char = rich_line[rich_pos]
+            end
+
+            # multi-char HTML entities in the rich line correspond to a single character in the raw line
+            if rich_char == '&'
+              multichar_mapping = [rich_pos]
+              until rich_char == ';'
+                rich_pos += 1
+                multichar_mapping << rich_pos
+                rich_char = rich_line[rich_pos]
+              end
+
+              mapping[raw_pos] = multichar_mapping
+            else
+              mapping[raw_pos] = rich_pos
+            end
+
+            rich_pos += 1
+          end
+
+          mapping
+        end
+      end
+
+      # Takes an array of integers, and returns an array of ranges covering the same integers
+      def collapse_ranges(positions)
+        return [] if positions.empty?
+        ranges = []
+
+        start = prev = positions[0]
+        range = start..prev
+        positions[1..-1].each do |pos|
+          if pos == prev + 1
+            range = start..pos
+            prev = pos
+          else
+            ranges << range
+            start = prev = pos
+            range = start..prev
+          end
+        end
+        ranges << range
+
+        ranges
+      end
+
+      # Inserts tags around the characters identified by the given range
+      def insert_around_range(text, range, before, after, offset = 0)
+        # Just to be sure
+        return offset if offset + range.end + 1 > text.length
+
+        text.insert(offset + range.begin, before)
+        offset += before.length
+
+        text.insert(offset + range.end + 1, after)
+        offset += after.length
+
+        offset
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/diff/line.rb b/lib/gitlab/diff/line.rb
index 0072194606e69b5c9da9f086c80bd7fa2a630e16..03730b435adc5fdceaf404198ee07ccf9da58ace 100644
--- a/lib/gitlab/diff/line.rb
+++ b/lib/gitlab/diff/line.rb
@@ -1,7 +1,8 @@
 module Gitlab
   module Diff
     class Line
-      attr_reader :type, :text, :index, :old_pos, :new_pos
+      attr_reader :type, :index, :old_pos, :new_pos
+      attr_accessor :text
 
       def initialize(text, type, index, old_pos, new_pos)
         @text, @type, @index = text, type, index
diff --git a/lib/gitlab/diff/parallel_diff.rb b/lib/gitlab/diff/parallel_diff.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c0db3559e3a174074fda0e1ed04f812fa9190871
--- /dev/null
+++ b/lib/gitlab/diff/parallel_diff.rb
@@ -0,0 +1,117 @@
+module Gitlab
+  module Diff
+    class ParallelDiff
+      attr_accessor :diff_file
+
+      def initialize(diff_file)
+        @diff_file = diff_file
+      end
+
+      def parallelize
+        lines = []
+        skip_next = false
+
+        diff_file.highlighted_diff_lines.each do |line|
+          full_line = line.text
+          type = line.type
+          line_code = generate_line_code(diff_file.file_path, line)
+          line_new = line.new_pos
+          line_old = line.old_pos
+
+          next_line = diff_file.next_line(line.index)
+
+          if next_line
+            next_line_code = generate_line_code(diff_file.file_path, next_line)
+            next_type = next_line.type
+            next_line = next_line.text
+          end
+
+          case type
+          when 'match', nil
+            # line in the right panel is the same as in the left one
+            lines << {
+              left: {
+                type:       type,
+                number:     line_old,
+                text:       full_line,
+                line_code:  line_code,
+              },
+              right: {
+                type:       type,
+                number:     line_new,
+                text:       full_line,
+                line_code:  line_code
+              }
+            }
+          when 'old'
+            case next_type
+            when 'new'
+              # Left side has text removed, right side has text added
+              lines << {
+                left: {
+                  type:       type,
+                  number:     line_old,
+                  text:       full_line,
+                  line_code:  line_code,
+                },
+                right: {
+                  type:       next_type,
+                  number:     line_new,
+                  text:       next_line,
+                  line_code:  next_line_code
+                }
+              }
+              skip_next = true
+            when 'old', 'nonewline', nil
+              # Left side has text removed, right side doesn't have any change
+              # No next line code, no new line number, no new line text
+              lines << {
+                left: {
+                  type:       type,
+                  number:     line_old,
+                  text:       full_line,
+                  line_code:  line_code,
+                },
+                right: {
+                  type:       next_type,
+                  number:     nil,
+                  text:       "",
+                  line_code:  nil
+                }
+              }
+            end
+          when 'new'
+            if skip_next
+              # Change has been already included in previous line so no need to do it again
+              skip_next = false
+              next
+            else
+              # Change is only on the right side, left side has no change
+              lines << {
+                left: {
+                  type:       nil,
+                  number:     nil,
+                  text:       "",
+                  line_code:  line_code,
+                },
+                right: {
+                  type:       type,
+                  number:     line_new,
+                  text:       full_line,
+                  line_code:  line_code
+                }
+              }
+            end
+          end
+        end
+        lines
+      end
+
+      private
+
+      def generate_line_code(file_path, line)
+        Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/diff/parser.rb b/lib/gitlab/diff/parser.rb
index 7015fe36c3dbdcb402d67d294e63b0ffdaefab43..3666063bf8b81c25b30de52250f9600fe5afcdf7 100644
--- a/lib/gitlab/diff/parser.rb
+++ b/lib/gitlab/diff/parser.rb
@@ -11,13 +11,10 @@ module Gitlab
         line_new = 1
         type = nil
 
-        lines_arr = ::Gitlab::InlineDiff.processing lines
-
-        lines_arr.each do |line|
+        @lines.each do |line|
           next if filename?(line)
 
-          full_line = html_escape(line.gsub(/\n/, ''))
-          full_line = ::Gitlab::InlineDiff.replace_markers full_line
+          full_line = line.gsub(/\n/, '')
 
           if line.match(/^@@ -/)
             type = "match"
@@ -29,6 +26,10 @@ module Gitlab
             lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
             line_obj_index += 1
             next
+          elsif line[0] == '\\'
+            type = 'nonewline'
+            lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
+            line_obj_index += 1
           else
             type = identification_type(line)
             lines_obj << Gitlab::Diff::Line.new(full_line, type, line_obj_index, line_old, line_new)
@@ -36,10 +37,13 @@ module Gitlab
           end
 
 
-          if line[0] == "+"
+          case line[0]
+          when "+"
             line_new += 1
-          elsif line[0] == "-"
+          when "-"
             line_old += 1
+          when "\\"
+            # No increment
           else
             line_new += 1
             line_old += 1
@@ -56,24 +60,21 @@ module Gitlab
       private
 
       def filename?(line)
-        line.start_with?('--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
-                         '--- /tmp/diffy', '+++ /tmp/diffy')
+        line.start_with?( '--- /dev/null', '+++ /dev/null', '--- a', '+++ b',
+                          '+++ a', # The line will start with `+++ a` in the reverse diff of an orphan commit
+                          '--- /tmp/diffy', '+++ /tmp/diffy')
       end
 
       def identification_type(line)
-        if line[0] == "+"
+        case line[0]
+        when "+"
           "new"
-        elsif line[0] == "-"
+        when "-"
           "old"
         else
           nil
         end
       end
-
-      def html_escape(str)
-        replacements = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;', "'" => '&#39;' }
-        str.gsub(/[&"'><]/, replacements)
-      end
     end
   end
 end
diff --git a/lib/gitlab/email/message/repository_push.rb b/lib/gitlab/email/message/repository_push.rb
index a2eb7a70bd2a8eb97e8931c5bdb68ab877967e22..a05ffeb9cd24515584b0f278a60fa41210e80195 100644
--- a/lib/gitlab/email/message/repository_push.rb
+++ b/lib/gitlab/email/message/repository_push.rb
@@ -9,6 +9,7 @@ module Gitlab
 
         delegate :namespace, :name_with_namespace, to: :project, prefix: :project
         delegate :name, to: :author, prefix: :author
+        delegate :username, to: :author, prefix: :author
 
         def initialize(notify, project_id, recipient, opts = {})
           raise ArgumentError, 'Missing options: author_id, ref, action' unless
diff --git a/lib/gitlab/github_import/importer.rb b/lib/gitlab/github_import/importer.rb
index 2b0afbc7b39dd14a603eaac2c33c3dabe2af96c4..663402e81975c5d3f2bc735423e50bcfa7025abb 100644
--- a/lib/gitlab/github_import/importer.rb
+++ b/lib/gitlab/github_import/importer.rb
@@ -1,6 +1,8 @@
 module Gitlab
   module GithubImport
     class Importer
+      include Gitlab::ShellAdapter
+
       attr_reader :project, :client
 
       def initialize(project)
@@ -12,10 +14,7 @@ module Gitlab
       end
 
       def execute
-        import_issues
-        import_pull_requests
-
-        true
+        import_issues && import_pull_requests && import_wiki
       end
 
       private
@@ -34,6 +33,10 @@ module Gitlab
             end
           end
         end
+
+        true
+      rescue ActiveRecord::RecordInvalid
+        false
       end
 
       def import_pull_requests
@@ -48,6 +51,10 @@ module Gitlab
             import_comments_on_diff(pull_request.number, merge_request)
           end
         end
+
+        true
+      rescue ActiveRecord::RecordInvalid
+        false
       end
 
       def import_comments(issue_number, noteable)
@@ -66,6 +73,22 @@ module Gitlab
           noteable.notes.create!(comment.attributes)
         end
       end
+
+      def import_wiki
+        unless project.wiki_enabled?
+          wiki = WikiFormatter.new(project)
+          gitlab_shell.import_repository(wiki.path_with_namespace, wiki.import_url)
+          project.update_attribute(:wiki_enabled, true)
+        end
+
+        true
+      rescue Gitlab::Shell::Error => e
+        if e.message =~ /repository not exported/
+          true
+        else
+          false
+        end
+      end
     end
   end
 end
diff --git a/lib/gitlab/github_import/project_creator.rb b/lib/gitlab/github_import/project_creator.rb
index 8c27ebd1ce8fa503528a9911558a6bfe1a7e53f9..474927069a50878ae21ddc7519bc0f4a4438bca9 100644
--- a/lib/gitlab/github_import/project_creator.rb
+++ b/lib/gitlab/github_import/project_creator.rb
@@ -20,7 +20,8 @@ module Gitlab
           visibility_level: repo.private ? Gitlab::VisibilityLevel::PRIVATE : Gitlab::VisibilityLevel::PUBLIC,
           import_type: "github",
           import_source: repo.full_name,
-          import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@")
+          import_url: repo.clone_url.sub("https://", "https://#{@session_data[:github_access_token]}@"),
+          wiki_enabled: !repo.has_wiki? # If repo has wiki we'll import it later
         ).execute
 
         project.create_import_data(data: { "github_session" => session_data } )
diff --git a/lib/gitlab/github_import/pull_request_formatter.rb b/lib/gitlab/github_import/pull_request_formatter.rb
index b7c47958cc7f58a4f6a0a5efb44e770964934e50..f96fed0f5cfb1359418a64bae9ce86d04e1a5b81 100644
--- a/lib/gitlab/github_import/pull_request_formatter.rb
+++ b/lib/gitlab/github_import/pull_request_formatter.rb
@@ -18,7 +18,7 @@ module Gitlab
       end
 
       def cross_project?
-        source_repo.fork == true
+        source_repo.id != target_repo.id
       end
 
       def number
@@ -73,6 +73,10 @@ module Gitlab
         project
       end
 
+      def target_repo
+        raw_data.base.repo
+      end
+
       def target_branch
         target_project.repository.find_branch(raw_data.base.ref)
       end
diff --git a/lib/gitlab/github_import/wiki_formatter.rb b/lib/gitlab/github_import/wiki_formatter.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6c592ff469c8d1f60a4b0b1f1d4a626354d86486
--- /dev/null
+++ b/lib/gitlab/github_import/wiki_formatter.rb
@@ -0,0 +1,19 @@
+module Gitlab
+  module GithubImport
+    class WikiFormatter
+      attr_reader :project
+
+      def initialize(project)
+        @project = project
+      end
+
+      def path_with_namespace
+        "#{project.path_with_namespace}.wiki"
+      end
+
+      def import_url
+        project.import_url.sub(/\.git\z/, ".wiki.git")
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/gitlab_import/importer.rb b/lib/gitlab/gitlab_import/importer.rb
index e24b94d615944df3eee683768bf20d111451cd54..59926084d075a78b70d0ca9c0a87397f142c4a97 100644
--- a/lib/gitlab/gitlab_import/importer.rb
+++ b/lib/gitlab/gitlab_import/importer.rb
@@ -12,7 +12,7 @@ module Gitlab
       end
 
       def execute
-        project_identifier = URI.encode(project.import_source, '/')
+        project_identifier = CGI.escape(project.import_source, '/')
 
         #Issues && Comments
         issues = client.issues(project_identifier)
diff --git a/lib/gitlab/highlight.rb b/lib/gitlab/highlight.rb
new file mode 100644
index 0000000000000000000000000000000000000000..4ddb4fea977ccd7276a41fea3e445a02c79b19b5
--- /dev/null
+++ b/lib/gitlab/highlight.rb
@@ -0,0 +1,38 @@
+module Gitlab
+  class Highlight
+    def self.highlight(blob_name, blob_content, nowrap: true)
+      new(blob_name, blob_content, nowrap: nowrap).highlight(blob_content, continue: false)
+    end
+
+    def self.highlight_lines(repository, ref, file_name)
+      blob = repository.blob_at(ref, file_name)
+      return [] unless blob
+
+      highlight(file_name, blob.data).lines.map!(&:html_safe)
+    end
+
+    def initialize(blob_name, blob_content, nowrap: true)
+      @formatter = rouge_formatter(nowrap: nowrap)
+      @lexer = Rouge::Lexer.guess(filename: blob_name, source: blob_content).new rescue Rouge::Lexers::PlainText
+    end
+
+    def highlight(text, continue: true)
+      @formatter.format(@lexer.lex(text, continue: continue)).html_safe
+    rescue
+      @formatter.format(Rouge::Lexers::PlainText.lex(text)).html_safe
+    end
+
+    private
+
+    def rouge_formatter(options = {})
+      options = options.reverse_merge(
+        nowrap: true,
+        cssclass: 'code highlight',
+        lineanchors: true,
+        lineanchorsid: 'LC'
+      )
+
+      Rouge::Formatters::HTMLGitlab.new(options)
+    end
+  end
+end
diff --git a/lib/gitlab/inline_diff.rb b/lib/gitlab/inline_diff.rb
deleted file mode 100644
index 44507bde25deaef0e8f16adab6ca7af2670c99d7..0000000000000000000000000000000000000000
--- a/lib/gitlab/inline_diff.rb
+++ /dev/null
@@ -1,104 +0,0 @@
-module Gitlab
-  class InlineDiff
-    class << self
-
-      START  = "#!idiff-start!#"
-      FINISH = "#!idiff-finish!#"
-
-      def processing(diff_arr)
-        indexes = _indexes_of_changed_lines diff_arr
-
-        indexes.each do |index|
-          first_line = diff_arr[index+1]
-          second_line = diff_arr[index+2]
-
-          # Skip inline diff if empty line was replaced with content
-          next if first_line == "-\n"
-
-          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
-
-      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_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
-
-          if (first_line[-i] != second_line[-i]) || "#{first_token}#{START}".size == shortest_line[1..-i].size
-            break
-          end
-        end
-
-        last_the_same_symbols += 1
-        first_line[last_the_same_symbols..-1]
-      end
-
-      def _indexes_of_changed_lines(diff_arr)
-        chain_of_first_symbols = ""
-        diff_arr.each_with_index do |line, i|
-          chain_of_first_symbols += line[0]
-        end
-        chain_of_first_symbols.gsub!(/[^\-\+]/, "#")
-
-        offset = 0
-        indexes = []
-        while index = chain_of_first_symbols.index("#-+#", offset)
-          indexes << index
-          offset = index + 1
-        end
-        indexes
-      end
-
-      def replace_markers(line)
-        line.gsub!(START, "<span class='idiff'>")
-        line.gsub!(FINISH, "</span>")
-        line
-      end
-    end
-  end
-end
diff --git a/lib/gitlab/ip_check.rb b/lib/gitlab/ip_check.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f2e9b50d225717c0b53b54d77c6acf120527ac6c
--- /dev/null
+++ b/lib/gitlab/ip_check.rb
@@ -0,0 +1,34 @@
+module Gitlab
+  class IpCheck
+
+    def initialize(ip)
+      @ip = ip
+
+      application_settings = ApplicationSetting.current
+      @ip_blocking_enabled =  application_settings.ip_blocking_enabled
+      @dnsbl_servers_list = application_settings.dnsbl_servers_list
+    end
+
+    def spam?
+      @ip_blocking_enabled && blacklisted?
+    end
+
+    private
+
+    def blacklisted?
+      on_dns_blacklist?
+    end
+
+    def on_dns_blacklist?
+      dnsbl_check = DNSXLCheck.new
+      prepare_dnsbl_list(dnsbl_check)
+      dnsbl_check.test(@ip)
+    end
+
+    def prepare_dnsbl_list(dnsbl_check)
+      @dnsbl_servers_list.split(',').map(&:strip).reject(&:empty?).each do |domain|
+        dnsbl_check.add_list(domain, 1)
+      end
+    end
+  end
+end
diff --git a/lib/gitlab/ldap/access.rb b/lib/gitlab/ldap/access.rb
index c438a3d167b8e1d1ac42d7c7403b0a5eb97f7595..da4435c7308d5a761aac91697bad5a65d8699b3b 100644
--- a/lib/gitlab/ldap/access.rb
+++ b/lib/gitlab/ldap/access.rb
@@ -5,7 +5,7 @@
 module Gitlab
   module LDAP
     class Access
-      attr_reader :adapter, :provider, :user
+      attr_reader :provider, :user
 
       def self.open(user, &block)
         Gitlab::LDAP::Adapter.open(user.ldap_identity.provider) do |adapter|
@@ -32,20 +32,20 @@ module Gitlab
       end
 
       def allowed?
-        if Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter)
+        if ldap_user
           return true unless ldap_config.active_directory
 
           # Block user in GitLab if he/she was blocked in AD
           if Gitlab::LDAP::Person.disabled_via_active_directory?(user.ldap_identity.extern_uid, adapter)
-            user.block
+            user.ldap_block
             false
           else
-            user.activate if user.blocked? && !ldap_config.block_auto_created_users
+            user.activate if user.ldap_blocked?
             true
           end
         else
           # Block the user if they no longer exist in LDAP/AD
-          user.block 
+          user.ldap_block
           false
         end
       rescue
@@ -59,6 +59,10 @@ module Gitlab
       def ldap_config
         Gitlab::LDAP::Config.new(provider)
       end
+
+      def ldap_user
+        @ldap_user ||= Gitlab::LDAP::Person.find_by_dn(user.ldap_identity.extern_uid, adapter)
+      end
     end
   end
 end
diff --git a/lib/gitlab/ldap/adapter.rb b/lib/gitlab/ldap/adapter.rb
index 577a890a7d9879f8cc9f51a454644757c1dd6ff6..df65179bfeadedb6d611c51dabb54b972eeca6e8 100644
--- a/lib/gitlab/ldap/adapter.rb
+++ b/lib/gitlab/ldap/adapter.rb
@@ -70,19 +70,25 @@ module Gitlab
       end
 
       def ldap_search(*args)
-        results = ldap.search(*args)
+        # Net::LDAP's `time` argument doesn't work. Use Ruby `Timeout` instead.
+        Timeout.timeout(config.timeout) do
+          results = ldap.search(*args)
 
-        if results.nil?
-          response = ldap.get_operation_result
+          if results.nil?
+            response = ldap.get_operation_result
 
-          unless response.code.zero?
-            Rails.logger.warn("LDAP search error: #{response.message}")
-          end
+            unless response.code.zero?
+              Rails.logger.warn("LDAP search error: #{response.message}")
+            end
 
-          []
-        else
-          results
+            []
+          else
+            results
+          end
         end
+      rescue Timeout::Error
+        Rails.logger.warn("LDAP search timed out after #{config.timeout} seconds")
+        []
       end
     end
   end
diff --git a/lib/gitlab/ldap/config.rb b/lib/gitlab/ldap/config.rb
index 101a3285f4bc8c121404a401a783786772b480c9..aff7ccb157f77387c330e26e09510bcad748af08 100644
--- a/lib/gitlab/ldap/config.rb
+++ b/lib/gitlab/ldap/config.rb
@@ -88,6 +88,10 @@ module Gitlab
         options['attributes']
       end
 
+      def timeout
+        options['timeout'].to_i
+      end
+
       protected
       def base_config
         Gitlab.config.ldap
diff --git a/lib/gitlab/ldap/user.rb b/lib/gitlab/ldap/user.rb
index aef08c97d1d7edf18a23b02f82a0eedcb5af40e1..e044f0ecc6d8fd847631e2c61c803802849b2e7b 100644
--- a/lib/gitlab/ldap/user.rb
+++ b/lib/gitlab/ldap/user.rb
@@ -30,28 +30,31 @@ module Gitlab
       end
 
       def find_by_uid_and_provider
-        self.class.find_by_uid_and_provider(
-          auth_hash.uid, auth_hash.provider)
+        self.class.find_by_uid_and_provider(auth_hash.uid, auth_hash.provider)
       end
 
       def find_by_email
-        ::User.find_by(email: auth_hash.email.downcase)
+        ::User.find_by(email: auth_hash.email.downcase) if auth_hash.has_email?
       end
 
       def update_user_attributes
-        return unless persisted?
+        if persisted?
+          if auth_hash.has_email?
+            gl_user.skip_reconfirmation!
+            gl_user.email = auth_hash.email
+          end
 
-        gl_user.skip_reconfirmation!
-        gl_user.email = auth_hash.email
+          # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
+          identity = gl_user.identities.find { |identity|  identity.provider == auth_hash.provider }
+          identity ||= gl_user.identities.build(provider: auth_hash.provider)
 
-        # find_or_initialize_by doesn't update `gl_user.identities`, and isn't autosaved.
-        identity = gl_user.identities.find { |identity|  identity.provider == auth_hash.provider }
-        identity ||= gl_user.identities.build(provider: auth_hash.provider)
+          # For a new identity set extern_uid to the LDAP DN
+          # For an existing identity with matching email but changed DN, update the DN.
+          # For an existing identity with no change in DN, this line changes nothing.
+          identity.extern_uid = auth_hash.uid
+        end
 
-        # For a new user set extern_uid to the LDAP DN
-        # For an existing user with matching email but changed DN, update the DN.
-        # For an existing user with no change in DN, this line changes nothing.
-        identity.extern_uid = auth_hash.uid
+        gl_user.ldap_email = auth_hash.has_email?
 
         gl_user
       end
diff --git a/lib/gitlab/markdown/pipeline.rb b/lib/gitlab/markdown/pipeline.rb
index 8f3f43c0e914d72151d91189783b6a49810834cb..699d8b9fc0718c9ba135ca26164830bde73cb6f4 100644
--- a/lib/gitlab/markdown/pipeline.rb
+++ b/lib/gitlab/markdown/pipeline.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Gitlab
   module Markdown
     class Pipeline
diff --git a/lib/gitlab/metrics.rb b/lib/gitlab/metrics.rb
index 44356a0e42cd11f65cb607211dc840e248f1adcf..88a265c6af286662598699ca1bbe565b5a61f3f6 100644
--- a/lib/gitlab/metrics.rb
+++ b/lib/gitlab/metrics.rb
@@ -13,7 +13,8 @@ module Gitlab
         timeout:               current_application_settings[:metrics_timeout],
         method_call_threshold: current_application_settings[:metrics_method_call_threshold],
         host:                  current_application_settings[:metrics_host],
-        port:                  current_application_settings[:metrics_port]
+        port:                  current_application_settings[:metrics_port],
+        sample_interval:       current_application_settings[:metrics_sample_interval] || 15
       }
     end
 
@@ -36,20 +37,6 @@ module Gitlab
       @pool
     end
 
-    # Returns a relative path and line number based on the last application call
-    # frame.
-    def self.last_relative_application_frame
-      frame = caller_locations.find do |l|
-        l.path.start_with?(RAILS_ROOT) && !l.path.start_with?(METRICS_ROOT)
-      end
-
-      if frame
-        return frame.path.sub(PATH_REGEX, ''), frame.lineno
-      else
-        return nil, nil
-      end
-    end
-
     def self.submit_metrics(metrics)
       prepared = prepare_metrics(metrics)
 
diff --git a/lib/gitlab/metrics/rack_middleware.rb b/lib/gitlab/metrics/rack_middleware.rb
index e7a2f26d48bd3e9d101eb10684faf5cafdab11b9..6f179789d3e3016929df37a3a4e0e57456d9af68 100644
--- a/lib/gitlab/metrics/rack_middleware.rb
+++ b/lib/gitlab/metrics/rack_middleware.rb
@@ -39,10 +39,8 @@ module Gitlab
       end
 
       def tag_controller(trans, env)
-        controller = env[CONTROLLER_KEY]
-        label      = "#{controller.class.name}##{controller.action_name}"
-
-        trans.add_tag(:action, label)
+        controller   = env[CONTROLLER_KEY]
+        trans.action = "#{controller.class.name}##{controller.action_name}"
       end
     end
   end
diff --git a/lib/gitlab/metrics/sampler.rb b/lib/gitlab/metrics/sampler.rb
index 1ea425bc904bbe4b1ad6dd43e28ef5aaf83f013c..fc709222a9b748b584b5865c86fda02fccf5dd70 100644
--- a/lib/gitlab/metrics/sampler.rb
+++ b/lib/gitlab/metrics/sampler.rb
@@ -7,9 +7,14 @@ module Gitlab
     # statistics, etc.
     class Sampler
       # interval - The sampling interval in seconds.
-      def initialize(interval = 15)
-        @interval = interval
-        @metrics  = []
+      def initialize(interval = Metrics.settings[:sample_interval])
+        interval_half = interval.to_f / 2
+
+        @interval       = interval
+        @interval_steps = (-interval_half..interval_half).step(0.1).to_a
+        @last_step      = nil
+
+        @metrics = []
 
         @last_minor_gc = Delta.new(GC.stat[:minor_gc_count])
         @last_major_gc = Delta.new(GC.stat[:major_gc_count])
@@ -26,7 +31,7 @@ module Gitlab
           Thread.current.abort_on_exception = true
 
           loop do
-            sleep(@interval)
+            sleep(sleep_interval)
 
             sample
           end
@@ -102,6 +107,23 @@ module Gitlab
       def sidekiq?
         Sidekiq.server?
       end
+
+      # Returns the sleep interval with a random adjustment.
+      #
+      # The random adjustment is put in place to ensure we:
+      #
+      # 1. Don't generate samples at the exact same interval every time (thus
+      #    potentially missing anything that happens in between samples).
+      # 2. Don't sample data at the same interval two times in a row.
+      def sleep_interval
+        while step = @interval_steps.sample
+          if step != @last_step
+            @last_step = step
+
+            return @interval + @last_step
+          end
+        end
+      end
     end
   end
 end
diff --git a/lib/gitlab/metrics/sidekiq_middleware.rb b/lib/gitlab/metrics/sidekiq_middleware.rb
index ad441decfa251670545e86ba10cb0e074c1fc9d2..fd98aa3412e265b0b301bf1f8dac839724491843 100644
--- a/lib/gitlab/metrics/sidekiq_middleware.rb
+++ b/lib/gitlab/metrics/sidekiq_middleware.rb
@@ -5,19 +5,14 @@ module Gitlab
     # This middleware is intended to be used as a server-side middleware.
     class SidekiqMiddleware
       def call(worker, message, queue)
-        trans = Transaction.new
+        trans = Transaction.new("#{worker.class.name}#perform")
 
         begin
           trans.run { yield }
         ensure
-          tag_worker(trans, worker)
           trans.finish
         end
       end
-
-      def tag_worker(trans, worker)
-        trans.add_tag(:action, "#{worker.class.name}#perform")
-      end
     end
   end
 end
diff --git a/lib/gitlab/metrics/subscribers/action_view.rb b/lib/gitlab/metrics/subscribers/action_view.rb
index 7c0105d543a5f8ff09a5bd82dbcc88012741365f..2e9dd4645e39d84719a7f4be0123d179168b635b 100644
--- a/lib/gitlab/metrics/subscribers/action_view.rb
+++ b/lib/gitlab/metrics/subscribers/action_view.rb
@@ -33,16 +33,8 @@ module Gitlab
 
         def tags_for(event)
           path = relative_path(event.payload[:identifier])
-          tags = { view: path }
 
-          file, line = Metrics.last_relative_application_frame
-
-          if file and line
-            tags[:file] = file
-            tags[:line] = line
-          end
-
-          tags
+          { view: path }
         end
 
         def current_transaction
diff --git a/lib/gitlab/metrics/transaction.rb b/lib/gitlab/metrics/transaction.rb
index 73131cc6ef25d11257d8107a0b81741a51ade100..2578ddc49f46837e92aae2373c7a3f0a488e3cca 100644
--- a/lib/gitlab/metrics/transaction.rb
+++ b/lib/gitlab/metrics/transaction.rb
@@ -6,11 +6,15 @@ module Gitlab
 
       attr_reader :tags, :values
 
+      attr_accessor :action
+
       def self.current
         Thread.current[THREAD_KEY]
       end
 
-      def initialize
+      # action - A String describing the action performed, usually the class
+      #          plus method name.
+      def initialize(action = nil)
         @metrics = []
 
         @started_at  = nil
@@ -18,20 +22,30 @@ module Gitlab
 
         @values = Hash.new(0)
         @tags   = {}
+        @action = action
+
+        @memory_before = 0
+        @memory_after  = 0
       end
 
       def duration
         @finished_at ? (@finished_at - @started_at) * 1000.0 : 0.0
       end
 
+      def allocated_memory
+        @memory_after - @memory_before
+      end
+
       def run
         Thread.current[THREAD_KEY] = self
 
-        @started_at = Time.now
+        @memory_before = System.memory_usage
+        @started_at    = Time.now
 
         yield
       ensure
-        @finished_at = Time.now
+        @memory_after = System.memory_usage
+        @finished_at  = Time.now
 
         Thread.current[THREAD_KEY] = nil
       end
@@ -60,7 +74,7 @@ module Gitlab
       end
 
       def track_self
-        values = { duration: duration }
+        values = { duration: duration, allocated_memory: allocated_memory }
 
         @values.each do |name, value|
           values[name] = value
@@ -70,7 +84,15 @@ module Gitlab
       end
 
       def submit
-        Metrics.submit_metrics(@metrics.map(&:to_hash))
+        metrics = @metrics.map do |metric|
+          hash = metric.to_hash
+
+          hash[:tags][:action] ||= @action if @action
+
+          hash
+        end
+
+        Metrics.submit_metrics(metrics)
       end
 
       def sidekiq?
diff --git a/lib/gitlab/o_auth/auth_hash.rb b/lib/gitlab/o_auth/auth_hash.rb
index ba31599432bf2d66a4bba0e115fc7dbfb8187d64..36e5c2670bbf04c1c8b0f3fe897a371f8156f58f 100644
--- a/lib/gitlab/o_auth/auth_hash.rb
+++ b/lib/gitlab/o_auth/auth_hash.rb
@@ -32,6 +32,10 @@ module Gitlab
         @password ||= Gitlab::Utils.force_utf8(Devise.friendly_token[0, 8].downcase)
       end
 
+      def has_email?
+        get_info(:email).present?
+      end
+
       private
 
       def info
@@ -46,8 +50,8 @@ module Gitlab
 
       def username_and_email
         @username_and_email ||= begin
-          username  = get_info(:username) || get_info(:nickname)
-          email     = get_info(:email)
+          username  = get_info(:username).presence || get_info(:nickname).presence
+          email     = get_info(:email).presence
 
           username ||= generate_username(email)             if email
           email    ||= generate_temporarily_email(username) if username
diff --git a/lib/gitlab/o_auth/user.rb b/lib/gitlab/o_auth/user.rb
index f1a362f5303a296099c4ed7c725312b9498605a1..d87a72f7ba34ecaf92b1a509d2608996d310ba81 100644
--- a/lib/gitlab/o_auth/user.rb
+++ b/lib/gitlab/o_auth/user.rb
@@ -111,7 +111,7 @@ module Gitlab
       def block_after_signup?
         if creating_linked_ldap_user?
           ldap_config.block_auto_created_users
-        else 
+        else
           Gitlab.config.omniauth.block_auto_created_users
         end
       end
@@ -135,15 +135,18 @@ module Gitlab
       def user_attributes
         # Give preference to LDAP for sensitive information when creating a linked account
         if creating_linked_ldap_user?
-          username = ldap_person.username
-          email = ldap_person.email.first
-        else
-          username = auth_hash.username
-          email = auth_hash.email
+          username = ldap_person.username.presence
+          email = ldap_person.email.first.presence
         end
-        
+
+        username ||= auth_hash.username
+        email ||= auth_hash.email
+
+        name = auth_hash.name
+        name = ::Namespace.clean_path(username) if name.strip.empty?
+
         {
-          name:                       auth_hash.name,
+          name:                       name,
           username:                   ::Namespace.clean_path(username),
           email:                      email,
           password:                   auth_hash.password,
diff --git a/lib/gitlab/reference_extractor.rb b/lib/gitlab/reference_extractor.rb
index 4164e998dd15c282822c5051bf4469597706cf24..4d830aa45e1cfcc6bdf589a38ea5a5863bd6f708 100644
--- a/lib/gitlab/reference_extractor.rb
+++ b/lib/gitlab/reference_extractor.rb
@@ -1,5 +1,3 @@
-require 'banzai'
-
 module Gitlab
   # Extract possible GFM references from an arbitrary String for further processing.
   class ReferenceExtractor < Banzai::ReferenceExtractor
diff --git a/lib/support/init.d/gitlab b/lib/support/init.d/gitlab
index c5f07c8b508a948796901b0cf6bcc5024360ba71..1633891c8a0749493e2f45175d3cd708a84f9ca7 100755
--- a/lib/support/init.d/gitlab
+++ b/lib/support/init.d/gitlab
@@ -38,6 +38,7 @@ web_server_pid_path="$pid_path/unicorn.pid"
 sidekiq_pid_path="$pid_path/sidekiq.pid"
 mail_room_enabled=false
 mail_room_pid_path="$pid_path/mail_room.pid"
+gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd)
 gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
 gitlab_workhorse_options="-listenUmask 0 -listenNetwork unix -listenAddr $socket_path/gitlab-workhorse.socket -authBackend http://127.0.0.1:8080 -authSocket $rails_socket -documentRoot $app_root/public"
 gitlab_workhorse_log="$app_root/log/gitlab-workhorse.log"
@@ -233,10 +234,12 @@ start_gitlab() {
   if [ "$gitlab_workhorse_status" = "0" ]; then
     echo "The gitlab-workhorse is already running with pid $spid, not restarting"
   else
-    # No need to remove a socket, gitlab-workhorse does this itself
+    # No need to remove a socket, gitlab-workhorse does this itself.
+    # Because gitlab-workhorse has multiple executables we need to fix
+    # the PATH.
     $app_root/bin/daemon_with_pidfile $gitlab_workhorse_pid_path  \
-      $app_root/../gitlab-workhorse/gitlab-workhorse \
-        $gitlab_workhorse_options \
+      /usr/bin/env PATH=$gitlab_workhorse_dir:$PATH \
+        gitlab-workhorse $gitlab_workhorse_options \
       >> $gitlab_workhorse_log 2>&1 &
   fi
 
diff --git a/lib/support/init.d/gitlab.default.example b/lib/support/init.d/gitlab.default.example
index 1937ca582b0548939c42d2aca1bab39b45357b2a..4e6e56ac2db5ad3f5e065fd41506bb649bd15a2a 100755
--- a/lib/support/init.d/gitlab.default.example
+++ b/lib/support/init.d/gitlab.default.example
@@ -30,6 +30,9 @@ web_server_pid_path="$pid_path/unicorn.pid"
 # The default is "$pid_path/sidekiq.pid"
 sidekiq_pid_path="$pid_path/sidekiq.pid"
 
+# The directory where the gitlab-workhorse binaries are. Usually
+# /home/git/gitlab-workhorse .
+gitlab_workhorse_dir=$(cd $app_root/../gitlab-workhorse && pwd)
 gitlab_workhorse_pid_path="$pid_path/gitlab-workhorse.pid"
 # The -listenXxx settings determine where gitlab-workhorse
 # listens for connections from NGINX. To listen on localhost:8181, write
diff --git a/lib/tasks/gitlab/task_helpers.rake b/lib/tasks/gitlab/task_helpers.rake
index ebe516ec879c71e67dff706a173d110ec926c91b..d33b5b31e189af40ab2c125d55ee919c6c473df8 100644
--- a/lib/tasks/gitlab/task_helpers.rake
+++ b/lib/tasks/gitlab/task_helpers.rake
@@ -2,6 +2,11 @@ module Gitlab
   class TaskAbortedByUserError < StandardError; end
 end
 
+String.disable_colorization = true unless STDOUT.isatty
+
+# Prevent StateMachine warnings from outputting during a cron task
+StateMachines::Machine.ignore_method_conflicts = true if ENV['CRON']
+
 namespace :gitlab do
 
   # Ask if the user wants to continue
diff --git a/spec/controllers/admin/identities_controller_spec.rb b/spec/controllers/admin/identities_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..c131d22a30a6078e02ddf359b5c5705daf90633f
--- /dev/null
+++ b/spec/controllers/admin/identities_controller_spec.rb
@@ -0,0 +1,26 @@
+require 'spec_helper'
+
+describe Admin::IdentitiesController do
+  let(:admin) { create(:admin) }
+  before { sign_in(admin) }
+
+  describe 'UPDATE identity' do
+    let(:user) { create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=myuser,ou=people,dc=example,dc=com') }
+
+    it 'repairs ldap blocks' do
+      expect_any_instance_of(RepairLdapBlockedUserService).to receive(:execute)
+
+      put :update, user_id: user.username, id: user.ldap_identity.id, identity: { provider: 'twitter' }
+    end
+  end
+
+  describe 'DELETE identity' do
+    let(:user) { create(:omniauth_user, provider: 'ldapmain', extern_uid: 'uid=myuser,ou=people,dc=example,dc=com') }
+
+    it 'repairs ldap blocks' do
+      expect_any_instance_of(RepairLdapBlockedUserService).to receive(:execute)
+
+      delete :destroy, user_id: user.username, id: user.ldap_identity.id
+    end
+  end
+end
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index 8b7af4d3a0aa94542ab494ca663693142ac4b409..5b1f65d7aff032c7366f24be60a11b32ee8916a7 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -34,17 +34,34 @@ describe Admin::UsersController do
   end
 
   describe 'PUT unblock/:id' do
-    let(:user) { create(:user) }
-
-    before do
-      user.block
+    context 'ldap blocked users' do
+      let(:user) { create(:omniauth_user, provider: 'ldapmain') }
+
+      before do
+        user.ldap_block
+      end
+
+      it 'will not unblock user' do
+        put :unblock, id: user.username
+        user.reload
+        expect(user.blocked?).to be_truthy
+        expect(flash[:alert]).to eq 'This user cannot be unlocked manually from GitLab'
+      end
     end
 
-    it 'unblocks user' do
-      put :unblock, id: user.username
-      user.reload
-      expect(user.blocked?).to be_falsey
-      expect(flash[:notice]).to eq 'Successfully unblocked'
+    context 'manually blocked users' do
+      let(:user) { create(:user) }
+
+      before do
+        user.block
+      end
+
+      it 'unblocks user' do
+        put :unblock, id: user.username
+        user.reload
+        expect(user.blocked?).to be_falsey
+        expect(flash[:notice]).to eq 'Successfully unblocked'
+      end
     end
   end
 
diff --git a/spec/controllers/sent_notification_controller_spec.rb b/spec/controllers/sent_notification_controller_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9ced397bd4a05864b5dbef191e862341feae7ddc
--- /dev/null
+++ b/spec/controllers/sent_notification_controller_spec.rb
@@ -0,0 +1,26 @@
+require 'rails_helper'
+
+describe SentNotificationsController, type: :controller do
+  let(:user)              { create(:user) }
+  let(:issue)             { create(:issue, author: user) }
+  let(:sent_notification) { create(:sent_notification, noteable: issue) }
+
+  describe 'GET #unsubscribe' do
+    it 'returns a 404 when calling without existing id' do
+      get(:unsubscribe, id: '0' * 32)
+
+      expect(response.status).to be 404
+    end
+
+    context 'calling with id' do
+      it 'shows a flash message to the user' do
+        get(:unsubscribe, id: sent_notification.reply_key)
+
+        expect(response.status).to be 302
+
+        expect(response).to redirect_to new_user_session_path
+        expect(controller).to set_flash[:notice].to(/unsubscribed/).now
+      end
+    end
+  end
+end
diff --git a/spec/factories.rb b/spec/factories.rb
index d6b4efa9a03d78b858ccf43b2e2d0cc54e57c9c0..2a81684dfcf0a6f0f7c6ba6369e9e747b8602f4f 100644
--- a/spec/factories.rb
+++ b/spec/factories.rb
@@ -212,4 +212,11 @@ FactoryGirl.define do
     provider 'ldapmain'
     extern_uid 'my-ldap-id'
   end
+
+  factory :sent_notification do
+    project
+    recipient factory: :user
+    noteable factory: :issue
+    reply_key "0123456789abcdef" * 2
+  end
 end
diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb
index ea0039d39e67e1d4cf53c213a417a88869a03f27..978a7d4cecb978fe1a53b44d16cf51aa6c0f8fb0 100644
--- a/spec/factories/broadcast_messages.rb
+++ b/spec/factories/broadcast_messages.rb
@@ -6,7 +6,6 @@
 #  message    :text             not null
 #  starts_at  :datetime
 #  ends_at    :datetime
-#  alert_type :integer
 #  created_at :datetime
 #  updated_at :datetime
 #  color      :string(255)
@@ -18,10 +17,17 @@
 FactoryGirl.define do
   factory :broadcast_message do
     message "MyText"
-    starts_at "2013-11-12 13:43:25"
-    ends_at "2013-11-12 13:43:25"
-    alert_type 1
-    color "#555555"
-    font "#BBBBBB"
+    starts_at Date.today
+    ends_at Date.tomorrow
+
+    trait :expired do
+      starts_at 5.days.ago
+      ends_at 3.days.ago
+    end
+
+    trait :future do
+      starts_at 5.days.from_now
+      ends_at 6.days.from_now
+    end
   end
 end
diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb
index f76e826f138c58c297a5fb5f9b9d21dc7d287edf..d2db77f6286a98540cddd5e9af62258d7151877f 100644
--- a/spec/factories/ci/builds.rb
+++ b/spec/factories/ci/builds.rb
@@ -30,6 +30,7 @@ FactoryGirl.define do
     name 'test'
     ref 'master'
     tag false
+    created_at 'Di 29. Okt 09:50:00 CET 2013'
     started_at 'Di 29. Okt 09:51:28 CET 2013'
     finished_at 'Di 29. Okt 09:53:28 CET 2013'
     commands 'ls -a'
@@ -42,6 +43,10 @@ FactoryGirl.define do
 
     commit factory: :ci_commit
 
+    trait :canceled do
+      status 'canceled'
+    end
+
     after(:build) do |build, evaluator|
       build.project = build.commit.project
     end
@@ -54,5 +59,11 @@ FactoryGirl.define do
     factory :ci_build_tag do
       tag true
     end
+
+    factory :ci_build_with_trace do
+      after(:create) do  |build, evaluator|
+        build.trace = 'BUILD TRACE'
+      end
+    end
   end
 end
diff --git a/spec/factories/ci/trigger_requests.rb b/spec/factories/ci/trigger_requests.rb
index db053c610cd58b6d8d2b799a2db9ae518181e01e..2c0d004d2675f4f5abc2465263e6ae9850db8ef2 100644
--- a/spec/factories/ci/trigger_requests.rb
+++ b/spec/factories/ci/trigger_requests.rb
@@ -3,6 +3,8 @@
 FactoryGirl.define do
   factory :ci_trigger_request, class: Ci::TriggerRequest do
     factory :ci_trigger_request_with_variables do
+      trigger factory: :ci_trigger
+
       variables do
         {
           TRIGGER_KEY: 'TRIGGER_VALUE'
diff --git a/spec/factories/ci/variables.rb b/spec/factories/ci/variables.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8f62d64411bdea433fbc06b6e9d1be79c7cf8355
--- /dev/null
+++ b/spec/factories/ci/variables.rb
@@ -0,0 +1,22 @@
+# == Schema Information
+#
+# Table name: ci_variables
+#
+#  id                   :integer          not null, primary key
+#  project_id           :integer          not null
+#  key                  :string(255)
+#  value                :text
+#  encrypted_value      :text
+#  encrypted_value_salt :string(255)
+#  encrypted_value_iv   :string(255)
+#  gl_project_id        :integer
+#
+
+# Read about factories at https://github.com/thoughtbot/factory_girl
+
+FactoryGirl.define do
+  factory :ci_variable, class: Ci::Variable do
+    sequence(:key) { |n| "VARIABLE_#{n}" }
+    value 'VARIABLE_VALUE'
+  end
+end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 240e56839df68451f666d9853a258235ba244c58..d37bd103714a9a5858145935f79fe68b6f0ea21c 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -80,7 +80,11 @@ describe "Builds" do
         visit namespace_project_build_path(@project.namespace, @project, @build)
       end
 
-      it { expect(page).to have_content 'Download artifacts' }
+      it 'has button to download artifacts' do
+        page.within('.artifacts') do
+          expect(page).to have_content 'Download'
+        end
+      end
     end
   end
 
@@ -111,7 +115,7 @@ describe "Builds" do
     before do
       @build.update_attributes(artifacts_file: artifacts_file)
       visit namespace_project_build_path(@project.namespace, @project, @build)
-      click_link 'Download artifacts'
+      page.within('.artifacts') { click_link 'Download' }
     end
 
     it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) }
diff --git a/spec/features/ci_lint_spec.rb b/spec/features/ci_lint_spec.rb
index e6e73e5e67ca34dd3ea2734fc30842bdf0d9c6a2..30e29d9d5526545c8483d3fdc455e7f21ab75b7e 100644
--- a/spec/features/ci_lint_spec.rb
+++ b/spec/features/ci_lint_spec.rb
@@ -35,5 +35,13 @@ describe 'CI Lint' do
         expect(page).to have_content('Error: Please provide content of .gitlab-ci.yml')
       end
     end
+
+    describe 'YAML revalidate' do
+      let(:yaml_content) { 'my yaml content' }
+
+      it 'loads previous YAML content after validation' do
+        expect(page).to have_field('content', with: 'my yaml content', type: 'textarea')
+      end
+    end
   end
 end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index e836d81c40be4ec0ff56ad5f376ddb1e2f9e4ca2..12fd8d372104edc79f5b5b50c95b0bb268d925b0 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -175,13 +175,15 @@ describe 'GitLab Markdown', feature: true do
     end
   end
 
-  context 'default pipeline' do
-    before(:all) do
-      @feat = MarkdownFeature.new
+  before(:all) do
+    @feat = MarkdownFeature.new
 
-      # `markdown` helper expects a `@project` variable
-      @project = @feat.project
+    # `markdown` helper expects a `@project` variable
+    @project = @feat.project
+  end
 
+  context 'default pipeline' do
+    before(:all) do
       @html = markdown(@feat.raw_markdown)
     end
 
@@ -221,6 +223,57 @@ describe 'GitLab Markdown', feature: true do
     end
   end
 
+  context 'wiki pipeline' do
+    before do
+      @project_wiki = @feat.project_wiki
+
+      file = Gollum::File.new(@project_wiki.wiki)
+      expect(file).to receive(:path).and_return('images/example.jpg')
+      expect(@project_wiki).to receive(:find_file).with('images/example.jpg').and_return(file)
+
+      @html = markdown(@feat.raw_markdown, { pipeline: :wiki, project_wiki: @project_wiki })
+    end
+
+    it_behaves_like 'all pipelines'
+
+    it 'includes RelativeLinkFilter' do
+      expect(doc).not_to parse_relative_links
+    end
+
+    it 'includes EmojiFilter' do
+      expect(doc).to parse_emoji
+    end
+
+    it 'includes TableOfContentsFilter' do
+      expect(doc).to create_header_links
+    end
+
+    it 'includes AutolinkFilter' do
+      expect(doc).to create_autolinks
+    end
+
+    it 'includes all reference filters' do
+      aggregate_failures do
+        expect(doc).to reference_users
+        expect(doc).to reference_issues
+        expect(doc).to reference_merge_requests
+        expect(doc).to reference_snippets
+        expect(doc).to reference_commit_ranges
+        expect(doc).to reference_commits
+        expect(doc).to reference_labels
+        expect(doc).to reference_milestones
+      end
+    end
+
+    it 'includes TaskListFilter' do
+      expect(doc).to parse_task_lists
+    end
+
+    it 'includes GollumTagsFilter' do
+      expect(doc).to parse_gollum_tags
+    end
+  end
+
   # Fake a `current_user` helper
   def current_user
     @feat.user
diff --git a/spec/fixtures/ci_build_artifacts.zip b/spec/fixtures/ci_build_artifacts.zip
new file mode 100644
index 0000000000000000000000000000000000000000..dae976d918e998171438058d8801d2ddd85ab4e1
Binary files /dev/null and b/spec/fixtures/ci_build_artifacts.zip differ
diff --git a/spec/fixtures/ci_build_artifacts_metadata.gz b/spec/fixtures/ci_build_artifacts_metadata.gz
new file mode 100644
index 0000000000000000000000000000000000000000..e6d17e4595d61ff48dcd459f48183fa7f7c75757
Binary files /dev/null and b/spec/fixtures/ci_build_artifacts_metadata.gz differ
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 0620096d6890a0c9b8946ae5abcb15413e9ad124..fe6d42acee256560713932f405849d5e640a3ffe 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -230,3 +230,12 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
   - [ ] Incomplete sub-task 2
   - [x] Complete sub-task 1
 - [X] Complete task 2
+
+#### Gollum Tags
+
+- [[linked-resource]]
+- [[link-text|linked-resource]]
+- [[http://example.com]]
+- [[link-text|http://example.com/pdfs/gollum.pdf]]
+- [[images/example.jpg]]
+- [[http://example.com/images/example.jpg]]
diff --git a/spec/fixtures/parallel_diff_result.yml b/spec/fixtures/parallel_diff_result.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a326b651aad74cfa9c5a8e46d4845aa2f6167e61
--- /dev/null
+++ b/spec/fixtures/parallel_diff_result.yml
@@ -0,0 +1,324 @@
+---
+- :left:
+    :type: match
+    :number: 6
+    :text: "@@ -6,12 +6,18 @@ module Popen"
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6
+  :right:
+    :type: match
+    :number: 6
+    :text: "@@ -6,12 +6,18 @@ module Popen"
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6
+- :left:
+    :type:
+    :number: 6
+    :text: |2
+       <span id="LC6" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6
+  :right:
+    :type:
+    :number: 6
+    :text: |2
+       <span id="LC6" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6
+- :left:
+    :type:
+    :number: 7
+    :text: |2
+       <span id="LC7" class="line">  <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7
+  :right:
+    :type:
+    :number: 7
+    :text: |2
+       <span id="LC7" class="line">  <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7
+- :left:
+    :type:
+    :number: 8
+    :text: |2
+       <span id="LC8" class="line">    <span class="k">unless</span> <span class="n">cmd</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8
+  :right:
+    :type:
+    :number: 8
+    :text: |2
+       <span id="LC8" class="line">    <span class="k">unless</span> <span class="n">cmd</span><span class="p">.</span><span class="nf">is_a?</span><span class="p">(</span><span class="no">Array</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8
+- :left:
+    :type: old
+    :number: 9
+    :text: |
+      -<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9
+  :right:
+    :type: new
+    :number: 9
+    :text: |
+      +<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9
+- :left:
+    :type:
+    :number: 10
+    :text: |2
+       <span id="LC10" class="line">    <span class="k">end</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10
+  :right:
+    :type:
+    :number: 10
+    :text: |2
+       <span id="LC10" class="line">    <span class="k">end</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10
+- :left:
+    :type:
+    :number: 11
+    :text: |2
+       <span id="LC11" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11
+  :right:
+    :type:
+    :number: 11
+    :text: |2
+       <span id="LC11" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11
+- :left:
+    :type:
+    :number: 12
+    :text: |2
+       <span id="LC12" class="line">    <span class="n">path</span> <span class="o">||=</span> <span class="no">Dir</span><span class="p">.</span><span class="nf">pwd</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12
+  :right:
+    :type:
+    :number: 12
+    :text: |2
+       <span id="LC12" class="line">    <span class="n">path</span> <span class="o">||=</span> <span class="no">Dir</span><span class="p">.</span><span class="nf">pwd</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12
+- :left:
+    :type: old
+    :number: 13
+    :text: |
+      -<span id="LC13" class="line">    <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span> <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span> <span class="p">}</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13
+  :right:
+    :type: old
+    :number:
+    :text: ''
+    :line_code:
+- :left:
+    :type: old
+    :number: 14
+    :text: |
+      -<span id="LC14" class="line">    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span> <span class="ss">chdir: </span><span class="n">path</span> <span class="p">}</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13
+  :right:
+    :type: new
+    :number: 13
+    :text: |
+      +<span id="LC13" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_13
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14
+  :right:
+    :type: new
+    :number: 14
+    :text: |
+      +<span id="LC14" class="line">    <span class="n">vars</span> <span class="o">=</span> <span class="p">{</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
+  :right:
+    :type: new
+    :number: 15
+    :text: |
+      +<span id="LC15" class="line">      <span class="s2">&quot;PWD&quot;</span> <span class="o">=&gt;</span> <span class="n">path</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16
+  :right:
+    :type: new
+    :number: 16
+    :text: |
+      +<span id="LC16" class="line">    <span class="p">}</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17
+  :right:
+    :type: new
+    :number: 17
+    :text: |
+      +<span id="LC17" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18
+  :right:
+    :type: new
+    :number: 18
+    :text: |
+      +<span id="LC18" class="line">    <span class="n">options</span> <span class="o">=</span> <span class="p">{</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19
+  :right:
+    :type: new
+    :number: 19
+    :text: |
+      +<span id="LC19" class="line">      <span class="ss">chdir: </span><span class="n">path</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20
+  :right:
+    :type: new
+    :number: 20
+    :text: |
+      +<span id="LC20" class="line">    <span class="p">}</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20
+- :left:
+    :type:
+    :number: 15
+    :text: |2
+       <span id="LC21" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21
+  :right:
+    :type:
+    :number: 21
+    :text: |2
+       <span id="LC21" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21
+- :left:
+    :type:
+    :number: 16
+    :text: |2
+       <span id="LC22" class="line">    <span class="k">unless</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22
+  :right:
+    :type:
+    :number: 22
+    :text: |2
+       <span id="LC22" class="line">    <span class="k">unless</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22
+- :left:
+    :type:
+    :number: 17
+    :text: |2
+       <span id="LC23" class="line">      <span class="no">FileUtils</span><span class="p">.</span><span class="nf">mkdir_p</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23
+  :right:
+    :type:
+    :number: 23
+    :text: |2
+       <span id="LC23" class="line">      <span class="no">FileUtils</span><span class="p">.</span><span class="nf">mkdir_p</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23
+- :left:
+    :type: match
+    :number: 19
+    :text: "@@ -19,6 +25,7 @@ module Popen"
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25
+  :right:
+    :type: match
+    :number: 25
+    :text: "@@ -19,6 +25,7 @@ module Popen"
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25
+- :left:
+    :type:
+    :number: 19
+    :text: |2
+       <span id="LC25" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25
+  :right:
+    :type:
+    :number: 25
+    :text: |2
+       <span id="LC25" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25
+- :left:
+    :type:
+    :number: 20
+    :text: |2
+       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
+  :right:
+    :type:
+    :number: 26
+    :text: |2
+       <span id="LC26" class="line">    <span class="vi">@cmd_output</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26
+- :left:
+    :type:
+    :number: 21
+    :text: |2
+       <span id="LC27" class="line">    <span class="vi">@cmd_status</span> <span class="o">=</span> <span class="mi">0</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27
+  :right:
+    :type:
+    :number: 27
+    :text: |2
+       <span id="LC27" class="line">    <span class="vi">@cmd_status</span> <span class="o">=</span> <span class="mi">0</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27
+- :left:
+    :type:
+    :number:
+    :text: ''
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28
+  :right:
+    :type: new
+    :number: 28
+    :text: |
+      +<span id="LC28" class="line"></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28
+- :left:
+    :type:
+    :number: 22
+    :text: |2
+       <span id="LC29" class="line">    <span class="no">Open3</span><span class="p">.</span><span class="nf">popen3</span><span class="p">(</span><span class="n">vars</span><span class="p">,</span> <span class="o">*</span><span class="n">cmd</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">stdin</span><span class="p">,</span> <span class="n">stdout</span><span class="p">,</span> <span class="n">stderr</span><span class="p">,</span> <span class="n">wait_thr</span><span class="o">|</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29
+  :right:
+    :type:
+    :number: 29
+    :text: |2
+       <span id="LC29" class="line">    <span class="no">Open3</span><span class="p">.</span><span class="nf">popen3</span><span class="p">(</span><span class="n">vars</span><span class="p">,</span> <span class="o">*</span><span class="n">cmd</span><span class="p">,</span> <span class="n">options</span><span class="p">)</span> <span class="k">do</span> <span class="o">|</span><span class="n">stdin</span><span class="p">,</span> <span class="n">stdout</span><span class="p">,</span> <span class="n">stderr</span><span class="p">,</span> <span class="n">wait_thr</span><span class="o">|</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29
+- :left:
+    :type:
+    :number: 23
+    :text: |2
+       <span id="LC30" class="line">      <span class="vi">@cmd_output</span> <span class="o">&lt;&lt;</span> <span class="n">stdout</span><span class="p">.</span><span class="nf">read</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30
+  :right:
+    :type:
+    :number: 30
+    :text: |2
+       <span id="LC30" class="line">      <span class="vi">@cmd_output</span> <span class="o">&lt;&lt;</span> <span class="n">stdout</span><span class="p">.</span><span class="nf">read</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30
+- :left:
+    :type:
+    :number: 24
+    :text: |2
+       <span id="LC31" class="line">      <span class="vi">@cmd_output</span> <span class="o">&lt;&lt;</span> <span class="n">stderr</span><span class="p">.</span><span class="nf">read</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31
+  :right:
+    :type:
+    :number: 31
+    :text: |2
+       <span id="LC31" class="line">      <span class="vi">@cmd_output</span> <span class="o">&lt;&lt;</span> <span class="n">stderr</span><span class="p">.</span><span class="nf">read</span></span>
+    :line_code: 2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index efc850eb7050fff526b39c31ce192eed878117b9..30e353148a8749e362cc1fa10935b641cebee0ca 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -285,6 +285,10 @@ describe ApplicationHelper do
     it 'allows the script tag to be excluded' do
       expect(element(skip_js: true)).not_to include 'script'
     end
+
+    it 'converts to Time' do
+      expect { helper.time_ago_with_tooltip(Date.today) }.not_to raise_error
+    end
   end
 
   describe 'render_markup' do
diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb
index b8bba36439aecd580b94471d884e7b5e412094bd..87849230dbe97b883591d92e7330ed486a82d239 100644
--- a/spec/helpers/blob_helper_spec.rb
+++ b/spec/helpers/blob_helper_spec.rb
@@ -1,22 +1,22 @@
 require 'spec_helper'
 
 describe BlobHelper do
-  describe 'highlight' do
-    let(:blob_name) { 'test.lisp' }
-    let(:no_context_content) { ":type \"assem\"))" }
-    let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" }
-    let(:split_content) { blob_content.split("\n") }
-    let(:multiline_content) do
-      %q(
-      def test(input):
-        """This is line 1 of a multi-line comment.
-        This is line 2.
-        """
-      )
-    end
+  let(:blob_name) { 'test.lisp' }
+  let(:no_context_content) { ":type \"assem\"))" }
+  let(:blob_content) { "(make-pathname :defaults name\n#{no_context_content}" }
+  let(:split_content) { blob_content.split("\n") }
+  let(:multiline_content) do
+    %q(
+    def test(input):
+      """This is line 1 of a multi-line comment.
+      This is line 2.
+      """
+    )
+  end
 
+  describe '#highlight' do
     it 'should return plaintext for unknown lexer context' do
-      result = highlight(blob_name, no_context_content, nowrap: true, continue: false)
+      result = helper.highlight(blob_name, no_context_content, nowrap: true)
       expect(result).to eq('<span id="LC1" class="line">:type &quot;assem&quot;))</span>')
     end
 
@@ -24,28 +24,17 @@ describe BlobHelper do
       expected = %Q[<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>
 <span id="LC2" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>]
 
-      expect(highlight(blob_name, blob_content, nowrap: true, continue: false)).to eq(expected)
-    end
-
-    it 'should highlight continued blocks' do
-      # Both lines have LC1 as ID since formatter doesn't support continue at the moment
-      expected = [
-        '<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
-        '<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
-      ]
-
-      result = split_content.map{ |content| highlight(blob_name, content, nowrap: true, continue: true) }
-      expect(result).to eq(expected)
+      expect(helper.highlight(blob_name, blob_content, nowrap: true)).to eq(expected)
     end
 
     it 'should highlight multi-line comments' do
-      result = highlight(blob_name, multiline_content, nowrap: true, continue: false)
+      result = helper.highlight(blob_name, multiline_content, nowrap: true)
       html = Nokogiri::HTML(result)
       lines = html.search('.s')
       expect(lines.count).to eq(3)
       expect(lines[0].text).to eq('"""This is line 1 of a multi-line comment.')
-      expect(lines[1].text).to eq('        This is line 2.')
-      expect(lines[2].text).to eq('        """')
+      expect(lines[1].text).to eq('      This is line 2.')
+      expect(lines[2].text).to eq('      """')
     end
 
     context 'diff highlighting' do
@@ -59,9 +48,23 @@ describe BlobHelper do
       end
 
       it 'should highlight each line properly' do
-        result = highlight(blob_name, blob_content, nowrap: true, continue: false)
+        result = helper.highlight(blob_name, blob_content, nowrap: true)
         expect(result).to eq(expected)
       end
     end
   end
+
+  describe "#highlighter" do
+    it 'should highlight continued blocks' do
+      # Both lines have LC1 as ID since formatter doesn't support continue at the moment
+      expected = [
+        '<span id="LC1" class="line"><span class="p">(</span><span class="nb">make-pathname</span> <span class="ss">:defaults</span> <span class="nv">name</span></span>',
+        '<span id="LC1" class="line"><span class="ss">:type</span> <span class="s">&quot;assem&quot;</span><span class="p">))</span></span>'
+      ]
+
+      highlighter = helper.highlighter(blob_name, blob_content, nowrap: true)
+      result = split_content.map{ |content| highlighter.highlight(content) }
+      expect(result).to eq(expected)
+    end
+  end
 end
diff --git a/spec/helpers/broadcast_messages_helper_spec.rb b/spec/helpers/broadcast_messages_helper_spec.rb
index c7c6f45d14439886bac8b57f17caf5bfad9b1a99..157cc4665a2b61d7f033b5dec2a47f3a55f67a17 100644
--- a/spec/helpers/broadcast_messages_helper_spec.rb
+++ b/spec/helpers/broadcast_messages_helper_spec.rb
@@ -1,22 +1,60 @@
 require 'spec_helper'
 
 describe BroadcastMessagesHelper do
-  describe 'broadcast_styling' do
-    let(:broadcast_message) { double(color: '', font: '') }
+  describe 'broadcast_message' do
+    it 'returns nil when no current message' do
+      expect(helper.broadcast_message(nil)).to be_nil
+    end
+
+    it 'includes the current message' do
+      current = double(message: 'Current Message')
+
+      allow(helper).to receive(:broadcast_message_style).and_return(nil)
+
+      expect(helper.broadcast_message(current)).to include 'Current Message'
+    end
+
+    it 'includes custom style' do
+      current = double(message: 'Current Message')
+
+      allow(helper).to receive(:broadcast_message_style).and_return('foo')
+
+      expect(helper.broadcast_message(current)).to include 'style="foo"'
+    end
+  end
+
+  describe 'broadcast_message_style' do
+    it 'defaults to no style' do
+      broadcast_message = spy
+
+      expect(helper.broadcast_message_style(broadcast_message)).to eq ''
+    end
+
+    it 'allows custom style' do
+      broadcast_message = double(color: '#f2dede', font: '#b94a48')
+
+      expect(helper.broadcast_message_style(broadcast_message)).
+        to match('background-color: #f2dede; color: #b94a48')
+    end
+  end
+
+  describe 'broadcast_message_status' do
+    it 'returns Active' do
+      message = build(:broadcast_message)
+
+      expect(helper.broadcast_message_status(message)).to eq 'Active'
+    end
+
+    it 'returns Expired' do
+      message = build(:broadcast_message, :expired)
 
-    context "default style" do
-      it "should have no style" do
-        expect(broadcast_styling(broadcast_message)).to eq ''
-      end
+      expect(helper.broadcast_message_status(message)).to eq 'Expired'
     end
 
-    context "customized style" do
-      let(:broadcast_message) { double(color: "#f2dede", font: '#b94a48') }
+    it 'returns Pending' do
+      message = build(:broadcast_message, :future)
 
-      it "should have a customized style" do
-        expect(broadcast_styling(broadcast_message)).
-          to match('background-color: #f2dede; color: #b94a48')
-      end
+      expect(helper.broadcast_message_status(message)).to eq 'Pending'
     end
   end
 end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index 7c96a74e5816c0329a4094f82cd63ee95f40f3fb..955d2852cfddfa9f34195e4badf5fa86580aefa7 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -4,10 +4,12 @@ describe DiffHelper do
   include RepoHelpers
 
   let(:project) { create(:project) }
+  let(:repository) { project.repository }
   let(:commit) { project.commit(sample_commit.id) }
   let(:diffs) { commit.diffs }
   let(:diff) { diffs.first }
-  let(:diff_file) { Gitlab::Diff::File.new(diff) }
+  let(:diff_refs) { [commit.parent, commit] }
+  let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs) }
 
   describe 'diff_hard_limit_enabled?' do
     it 'should return true if param is provided' do
@@ -44,55 +46,41 @@ describe DiffHelper do
 
   describe 'safe_diff_files' do
     it 'should return all files from a commit that is smaller than safe limits' do
-      expect(safe_diff_files(diffs).length).to eq(2)
+      expect(safe_diff_files(diffs, diff_refs).length).to eq(2)
     end
 
     it 'should return only the first file if the diff line count in the 2nd file takes the total beyond safe limits' do
       allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
-      expect(safe_diff_files(diffs).length).to eq(1)
+      expect(safe_diff_files(diffs, diff_refs).length).to eq(1)
     end
 
     it 'should return all files from a commit that is beyond safe limit for numbers of lines if force diff is true' do
       allow(controller).to receive(:params) { { force_show_diff: true } }
       allow(diffs[1].diff).to receive(:lines).and_return([""] * 4999) #simulate 4999 lines
-      expect(safe_diff_files(diffs).length).to eq(2)
+      expect(safe_diff_files(diffs, diff_refs).length).to eq(2)
     end
 
     it 'should return only the first file if the diff line count in the 2nd file takes the total beyond hard limits' do
       allow(controller).to receive(:params) { { force_show_diff: true } }
       allow(diffs[1].diff).to receive(:lines).and_return([""] * 49999) #simulate 49999 lines
-      expect(safe_diff_files(diffs).length).to eq(1)
+      expect(safe_diff_files(diffs, diff_refs).length).to eq(1)
     end
 
     it 'should return only a safe number of file diffs if a commit touches more files than the safe limits' do
       large_diffs = diffs * 100 #simulate 200 diffs
-      expect(safe_diff_files(large_diffs).length).to eq(100)
+      expect(safe_diff_files(large_diffs, diff_refs).length).to eq(100)
     end
 
     it 'should return all file diffs if a commit touches more files than the safe limits but force diff is true' do
       allow(controller).to receive(:params) { { force_show_diff: true } }
       large_diffs = diffs * 100 #simulate 200 diffs
-      expect(safe_diff_files(large_diffs).length).to eq(200)
+      expect(safe_diff_files(large_diffs, diff_refs).length).to eq(200)
     end
 
     it 'should return a limited file diffs if a commit touches more files than the hard limits and force diff is true' do
       allow(controller).to receive(:params) { { force_show_diff: true } }
       very_large_diffs = diffs * 1000 #simulate 2000 diffs
-      expect(safe_diff_files(very_large_diffs).length).to eq(1000)
-    end
-  end
-
-  describe 'parallel_diff' do
-    it 'should return an array of arrays containing the parsed diff' do
-      expect(parallel_diff(diff_file, 0)).
-        to match_array(parallel_diff_result_array)
-    end
-  end
-
-  describe 'generate_line_code' do
-    it 'should generate correct line code' do
-      expect(generate_line_code(diff_file.file_path, diff_file.diff_lines.first)).
-        to eq('2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6')
+      expect(safe_diff_files(very_large_diffs, diff_refs).length).to eq(1000)
     end
   end
 
@@ -126,39 +114,11 @@ describe DiffHelper do
       expect(diff_line_content(diff_file.diff_lines.first.text)).
         to eq('@@ -6,12 +6,18 @@ module Popen')
       expect(diff_line_content(diff_file.diff_lines.first.type)).to eq('match')
-      expect(diff_line_content(diff_file.diff_lines.first.new_pos)).to eq(6)
+      expect(diff_file.diff_lines.first.new_pos).to eq(6)
     end
-  end
 
-  def parallel_diff_result_array
-    [
-      ["match", 6, "@@ -6,12 +6,18 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", "match", 6, "@@ -6,12 +6,18 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6"],
-      [nil, 6, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6", nil, 6, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_6_6"], [nil, 7, "   def popen(cmd, path=nil)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7", nil, 7, "   def popen(cmd, path=nil)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_7_7"],
-      [nil, 8, "     unless cmd.is_a?(Array)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8", nil, 8, "     unless cmd.is_a?(Array)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_8_8"],
-      ["old", 9, "-      raise <span class='idiff'></span>&quot;System commands must be given as an array of strings&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_9_9", "new", 9, "+      raise <span class='idiff'>RuntimeError, </span>&quot;System commands must be given as an array of strings&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_9"],
-      [nil, 10, "     end", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10", nil, 10, "     end", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_10_10"],
-      [nil, 11, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11", nil, 11, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_11_11"],
-      [nil, 12, "     path ||= Dir.pwd", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12", nil, 12, "     path ||= Dir.pwd", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_12_12"],
-      ["old", 13, "-    vars = { &quot;PWD&quot; =&gt; path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_13_13", "old", nil, "&nbsp;", nil],
-      ["old", 14, "-    options = { chdir: path }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_13", "new", 13, "+", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_13"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14", "new", 14, "+    vars = {", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_14"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15", "new", 15, "+      &quot;PWD&quot; =&gt; path", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_15"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16", "new", 16, "+    }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_16"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17", "new", 17, "+", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_17"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18", "new", 18, "+    options = {", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_18"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19", "new", 19, "+      chdir: path", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_19"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20", "new", 20, "+    }", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_20"],
-      [nil, 15, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21", nil, 21, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_15_21"],
-      [nil, 16, "     unless File.directory?(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22", nil, 22, "     unless File.directory?(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_16_22"],
-      [nil, 17, "       FileUtils.mkdir_p(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23", nil, 23, "       FileUtils.mkdir_p(path)", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_17_23"],
-      ["match", 19, "@@ -19,6 +25,7 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", "match", 25, "@@ -19,6 +25,7 @@ module Popen", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25"],
-      [nil, 19, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25", nil, 25, " ", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_19_25"],
-      [nil, 20, "     @cmd_output = &quot;&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26", nil, 26, "     @cmd_output = &quot;&quot;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_20_26"],
-      [nil, 21, "     @cmd_status = 0", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27", nil, 27, "     @cmd_status = 0", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_21_27"],
-      [nil, nil, "&nbsp;", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28", "new", 28, "+", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_28"],
-      [nil, 22, "     Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29", nil, 29, "     Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_22_29"],
-      [nil, 23, "       @cmd_output &lt;&lt; stdout.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30", nil, 30, "       @cmd_output &lt;&lt; stdout.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_23_30"],
-      [nil, 24, "       @cmd_output &lt;&lt; stderr.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31", nil, 31, "       @cmd_output &lt;&lt; stderr.read", "2f6fcd96b88b36ce98c38da085c795a27d92a3dd_24_31"]
-    ]
+    it 'should return safe HTML' do
+      expect(diff_line_content(diff_file.diff_lines.first.text)).to be_html_safe
+    end
   end
 end
diff --git a/spec/helpers/gitlab_markdown_helper_spec.rb b/spec/helpers/gitlab_markdown_helper_spec.rb
index 762ec25c4f5555305e1c741f6f852d272ecf179d..9a05b21335cee75c830b5745dc47ad4678479883 100644
--- a/spec/helpers/gitlab_markdown_helper_spec.rb
+++ b/spec/helpers/gitlab_markdown_helper_spec.rb
@@ -121,12 +121,13 @@ describe GitlabMarkdownHelper do
     before do
       @wiki = double('WikiPage')
       allow(@wiki).to receive(:content).and_return('wiki content')
+      helper.instance_variable_set(:@project_wiki, @wiki)
     end
 
-    it "should use GitLab Flavored Markdown for markdown files" do
+    it "should use Wiki pipeline for markdown files" do
       allow(@wiki).to receive(:format).and_return(:markdown)
 
-      expect(helper).to receive(:markdown).with('wiki content')
+      expect(helper).to receive(:markdown).with('wiki content', pipeline: :wiki, project_wiki: @wiki)
 
       helper.render_wiki_content(@wiki)
     end
diff --git a/spec/initializers/settings_spec.rb b/spec/initializers/settings_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e58f2c80e957dc0dbef9861f9992da0a1d455881
--- /dev/null
+++ b/spec/initializers/settings_spec.rb
@@ -0,0 +1,44 @@
+require_relative '../../config/initializers/1_settings'
+
+describe Settings, lib: true do
+
+  describe '#host_without_www' do
+    context 'URL with protocol' do
+      it 'returns the host' do
+        expect(Settings.host_without_www('http://foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('http://www.foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('http://secure.foo.com')).to eq 'secure.foo.com'
+        expect(Settings.host_without_www('http://www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+
+        expect(Settings.host_without_www('https://foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('https://www.foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('https://secure.foo.com')).to eq 'secure.foo.com'
+        expect(Settings.host_without_www('https://secure.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'secure.gravatar.com'
+      end
+    end
+
+    context 'URL without protocol' do
+      it 'returns the host' do
+        expect(Settings.host_without_www('foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('www.foo.com')).to eq 'foo.com'
+        expect(Settings.host_without_www('secure.foo.com')).to eq 'secure.foo.com'
+        expect(Settings.host_without_www('www.gravatar.com/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+      end
+
+      context 'URL with user/port' do
+        it 'returns the host' do
+          expect(Settings.host_without_www('bob:pass@foo.com:8080')).to eq 'foo.com'
+          expect(Settings.host_without_www('bob:pass@www.foo.com:8080')).to eq 'foo.com'
+          expect(Settings.host_without_www('bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com'
+          expect(Settings.host_without_www('bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+
+          expect(Settings.host_without_www('http://bob:pass@foo.com:8080')).to eq 'foo.com'
+          expect(Settings.host_without_www('http://bob:pass@www.foo.com:8080')).to eq 'foo.com'
+          expect(Settings.host_without_www('http://bob:pass@secure.foo.com:8080')).to eq 'secure.foo.com'
+          expect(Settings.host_without_www('http://bob:pass@www.gravatar.com:8080/avatar/%{hash}?s=%{size}&d=identicon')).to eq 'gravatar.com'
+        end
+      end
+    end
+  end
+
+end
diff --git a/spec/javascripts/issue_spec.js.coffee b/spec/javascripts/issue_spec.js.coffee
index b85fadcbe82e0169eba50b89e53632c0d2d7740a..86ba9dd8e9665b9c5db7d0287648123b81a765dd 100644
--- a/spec/javascripts/issue_spec.js.coffee
+++ b/spec/javascripts/issue_spec.js.coffee
@@ -44,7 +44,7 @@ describe 'reopen/close issue', ->
     expect($('div.status-box-closed')).toBeVisible()
     expect($('div.status-box-open')).toBeHidden()
 
-  it 'fails to closes an issue with success:false', ->
+  it 'fails to close an issue with success:false', ->
 
     spyOn(jQuery, 'ajax').and.callFake (req) ->
       expect(req.type).toBe('PUT')
diff --git a/spec/lib/banzai/filter/gollum_tags_filter_spec.rb b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..38baa81995767ac947238e82f1fbe6ee8877b657
--- /dev/null
+++ b/spec/lib/banzai/filter/gollum_tags_filter_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe Banzai::Filter::GollumTagsFilter, lib: true do
+  include FilterSpecHelper
+
+  let(:project) { create(:project) }
+  let(:user) { double }
+  let(:project_wiki) { ProjectWiki.new(project, user) }
+
+  describe 'validation' do
+    it 'ensure that a :project_wiki key exists in context' do
+      expect { filter("See [[images/image.jpg]]", {}) }.to raise_error ArgumentError, "Missing context keys for Banzai::Filter::GollumTagsFilter: :project_wiki"
+    end
+  end
+
+  context 'linking internal images' do
+    it 'creates img tag if image exists' do
+      file = Gollum::File.new(project_wiki.wiki)
+      expect(file).to receive(:path).and_return('images/image.jpg')
+      expect(project_wiki).to receive(:find_file).with('images/image.jpg').and_return(file)
+
+      tag = '[[images/image.jpg]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('img')['src']).to eq "#{project_wiki.wiki_base_path}/images/image.jpg"
+    end
+
+    it 'does not creates img tag if image does not exist' do
+      expect(project_wiki).to receive(:find_file).with('images/image.jpg').and_return(nil)
+
+      tag = '[[images/image.jpg]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.css('img').size).to eq 0
+    end
+  end
+
+  context 'linking external images' do
+    it 'creates img tag for valid URL' do
+      tag = '[[http://example.com/image.jpg]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('img')['src']).to eq "http://example.com/image.jpg"
+    end
+
+    it 'does not creates img tag for invalid URL' do
+      tag = '[[http://example.com/image.pdf]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.css('img').size).to eq 0
+    end
+  end
+
+  context 'linking external resources' do
+    it "the created link's text will be equal to the resource's text" do
+      tag = '[[http://example.com]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('a').text).to eq 'http://example.com'
+      expect(doc.at_css('a')['href']).to eq 'http://example.com'
+    end
+
+    it "the created link's text will be link-text" do
+      tag = '[[link-text|http://example.com/pdfs/gollum.pdf]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('a').text).to eq 'link-text'
+      expect(doc.at_css('a')['href']).to eq 'http://example.com/pdfs/gollum.pdf'
+    end
+  end
+
+  context 'linking internal resources' do
+    it "the created link's text will be equal to the resource's text" do
+      tag = '[[wiki-slug]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('a').text).to eq 'wiki-slug'
+      expect(doc.at_css('a')['href']).to eq 'wiki-slug'
+    end
+
+    it "the created link's text will be link-text" do
+      tag = '[[link-text|wiki-slug]]'
+      doc = filter("See #{tag}", project_wiki: project_wiki)
+
+      expect(doc.at_css('a').text).to eq 'link-text'
+      expect(doc.at_css('a')['href']).to eq 'wiki-slug'
+    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 d15100fc6d8c075de90677c6129d4bb0d24c8fcc..f3394910c5b34d63e0a2fd6dd4b552826dbeac65 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -336,7 +336,7 @@ module Ci
     describe "Caches" do
       it "returns cache when defined globally" do
         config = YAML.dump({
-                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
                              rspec: {
                                script: "rspec"
                              }
@@ -348,13 +348,14 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["logs/", "binaries/"],
           untracked: true,
+          key: 'key',
         )
       end
 
       it "returns cache when defined in a job" do
         config = YAML.dump({
                              rspec: {
-                               cache: { paths: ["logs/", "binaries/"], untracked: true },
+                               cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'key' },
                                script: "rspec"
                              }
                            })
@@ -365,15 +366,16 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["logs/", "binaries/"],
           untracked: true,
+          key: 'key',
         )
       end
 
       it "overwrite cache when defined for a job and globally" do
         config = YAML.dump({
-                             cache: { paths: ["logs/", "binaries/"], untracked: true },
+                             cache: { paths: ["logs/", "binaries/"], untracked: true, key: 'global' },
                              rspec: {
                                script: "rspec",
-                               cache: { paths: ["test/"], untracked: false },
+                               cache: { paths: ["test/"], untracked: false, key: 'local' },
                              }
                            })
 
@@ -383,6 +385,7 @@ module Ci
         expect(config_processor.builds_for_stage_and_ref("test", "master").first[:options][:cache]).to eq(
           paths: ["test/"],
           untracked: false,
+          key: 'local',
         )
       end
     end
@@ -615,6 +618,20 @@ module Ci
         end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:paths parameter should be an array of strings")
       end
 
+      it "returns errors if cache:key is not a string" do
+        config = YAML.dump({ cache: { key: 1 }, rspec: { script: "test" } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "cache:key parameter should be a string")
+      end
+
+      it "returns errors if job cache:key is not an a string" do
+        config = YAML.dump({ types: ["build", "test"], rspec: { script: "test", cache: { key: 1 } } })
+        expect do
+          GitlabCiYamlProcessor.new(config)
+        end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: cache:key parameter should be a string")
+      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
diff --git a/spec/lib/dnsxl_check_spec.rb b/spec/lib/dnsxl_check_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a35a1be0c9002021977b115b09973ccd7505d351
--- /dev/null
+++ b/spec/lib/dnsxl_check_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+require 'ostruct'
+
+describe 'DNSXLCheck', lib: true, no_db: true do
+  let(:spam_ip)    { '127.0.0.2' }
+  let(:no_spam_ip) { '127.0.0.3' }
+  let(:invalid_ip) { 'a.b.c.d' }
+  let!(:dnsxl_check) { DNSXLCheck.create_from_list([OpenStruct.new({ domain: 'test', weight: 1 })]) }
+
+  before(:context) do
+    class DNSXLCheck::Resolver
+      class << self
+        alias_method :old_search, :search
+        def search(query)
+          return false if query.match(/always\.failing\.domain\z/)
+          return true  if query.match(/\A2\.0\.0\.127\./)
+          return false if query.match(/\A3\.0\.0\.127\./)
+        end
+      end
+    end
+  end
+
+  describe '#test' do
+    before do
+      dnsxl_check.threshold = 0.75
+      dnsxl_check.add_list('always.failing.domain', 1)
+    end
+
+    context 'when threshold is used' do
+      before { dnsxl_check.use_threshold= true }
+
+      it { expect(dnsxl_check.test(spam_ip)).to be_falsey }
+    end
+
+    context 'when threshold is not used' do
+      before { dnsxl_check.use_threshold= false }
+
+      it { expect(dnsxl_check.test(spam_ip)).to be_truthy }
+    end
+  end
+
+  describe '#test_with_threshold' do
+    it { expect{ dnsxl_check.test_with_threshold(invalid_ip) }.to raise_error(ArgumentError) }
+
+    it { expect(dnsxl_check.test_with_threshold(spam_ip)).to    be_truthy }
+    it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
+  end
+
+  describe '#test_strict' do
+    before do
+      dnsxl_check.threshold = 1
+      dnsxl_check.add_list('always.failing.domain', 1)
+    end
+
+    it { expect{ dnsxl_check.test_strict(invalid_ip) }.to raise_error(ArgumentError) }
+
+    it { expect(dnsxl_check.test_with_threshold(spam_ip)).to    be_falsey }
+    it { expect(dnsxl_check.test_with_threshold(no_spam_ip)).to be_falsey }
+    it { expect(dnsxl_check.test_strict(spam_ip)).to    be_truthy }
+    it { expect(dnsxl_check.test_strict(no_spam_ip)).to be_falsey }
+  end
+
+  describe '#threshold=' do
+    it { expect{ dnsxl_check.threshold = 0   }.to     raise_error(ArgumentError) }
+    it { expect{ dnsxl_check.threshold = 1.1 }.to     raise_error(ArgumentError) }
+    it { expect{ dnsxl_check.threshold = 0.5 }.not_to raise_error }
+  end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..41257103eadbae0377580ab449a1547b18923c52
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -0,0 +1,168 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
+  let(:entries) do
+    { 'path/' => {},
+      'path/dir_1/' => {},
+      'path/dir_1/file_1' => {},
+      'path/dir_1/file_b' => {},
+      'path/dir_1/subdir/' => {},
+      'path/dir_1/subdir/subfile' => {},
+      'path/second_dir' => {},
+      'path/second_dir/dir_3/file_2' => {},
+      'path/second_dir/dir_3/file_3'=> {},
+      'another_directory/'=> {},
+      'another_file' => {},
+      '/file/with/absolute_path' => {} }
+  end
+
+  def path(example)
+    entry(example.metadata[:path])
+  end
+
+  def entry(path)
+    described_class.new(path, entries)
+  end
+
+  describe '/file/with/absolute_path', path: '/file/with/absolute_path' do
+    subject { |example| path(example) }
+
+    it { is_expected.to be_file }
+    it { is_expected.to have_parent }
+
+    describe '#basename' do
+      subject { |example| path(example).basename }
+      it { is_expected.to eq 'absolute_path' }
+    end
+  end
+
+  describe 'path/dir_1/', path: 'path/dir_1/' do
+    subject { |example| path(example) }
+    it { is_expected.to have_parent }
+    it { is_expected.to be_directory }
+
+    describe '#basename' do
+      subject { |example| path(example).basename }
+      it { is_expected.to eq 'dir_1/' }
+    end
+
+    describe '#name' do
+      subject { |example| path(example).name }
+      it { is_expected.to eq 'dir_1' }
+    end
+
+    describe '#parent' do
+      subject { |example| path(example).parent }
+      it { is_expected.to eq entry('path/') }
+    end
+
+    describe '#children' do
+      subject { |example| path(example).children }
+
+      it { is_expected.to all(be_an_instance_of described_class) }
+      it do
+        is_expected.to contain_exactly entry('path/dir_1/file_1'),
+                                       entry('path/dir_1/file_b'),
+                                       entry('path/dir_1/subdir/')
+      end
+    end
+
+    describe '#files' do
+      subject { |example| path(example).files }
+
+      it { is_expected.to all(be_file) }
+      it { is_expected.to all(be_an_instance_of described_class) }
+      it do
+        is_expected.to contain_exactly entry('path/dir_1/file_1'),
+                                       entry('path/dir_1/file_b')
+      end
+    end
+
+    describe '#directories' do
+      context 'without options' do
+        subject { |example| path(example).directories }
+
+        it { is_expected.to all(be_directory) }
+        it { is_expected.to all(be_an_instance_of described_class) }
+        it { is_expected.to contain_exactly entry('path/dir_1/subdir/') }
+      end
+
+      context 'with option parent: true' do
+        subject { |example| path(example).directories(parent: true) }
+
+        it { is_expected.to all(be_directory) }
+        it { is_expected.to all(be_an_instance_of described_class) }
+        it do
+          is_expected.to contain_exactly entry('path/dir_1/subdir/'),
+                                         entry('path/')
+        end
+      end
+
+      describe '#nodes' do
+        subject { |example| path(example).nodes }
+        it { is_expected.to eq 2 }
+      end
+
+      describe '#exists?' do
+        subject { |example| path(example).exists? }
+        it { is_expected.to be true }
+      end
+
+      describe '#empty?' do
+        subject { |example| path(example).empty? }
+        it { is_expected.to be false }
+      end
+    end
+  end
+
+  describe 'empty path', path: '' do
+    subject { |example| path(example) }
+    it { is_expected.to_not have_parent }
+
+    describe '#children' do
+      subject { |example| path(example).children }
+      it { expect(subject.count).to eq 3 }
+    end
+
+  end
+
+  describe 'path/dir_1/subdir/subfile', path: 'path/dir_1/subdir/subfile' do
+    describe '#nodes' do
+      subject { |example| path(example).nodes }
+      it { is_expected.to eq 4 }
+    end
+  end
+
+  describe 'non-existent/', path: 'non-existent/' do
+    describe '#empty?' do
+      subject { |example| path(example).empty? }
+      it { is_expected.to be true }
+    end
+
+    describe '#exists?' do
+      subject { |example| path(example).exists? }
+      it { is_expected.to be false }
+    end
+  end
+
+  describe 'another_directory/', path: 'another_directory/' do
+    describe '#empty?' do
+      subject { |example| path(example).empty? }
+      it { is_expected.to be true }
+    end
+  end
+
+  describe '#metadata' do
+    let(:entries) do
+      { 'path/' => { name: '/path/' },
+        'path/file1' => { name: '/path/file1' },
+        'path/file2' => { name: '/path/file2' } }
+    end
+
+    subject do
+      described_class.new('path/file1', entries).metadata[:name]
+    end
+
+    it { is_expected.to eq '/path/file1' }
+  end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..828eedfa7b03c923c8faafd2656c4818424d2568
--- /dev/null
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata_spec.rb
@@ -0,0 +1,84 @@
+require 'spec_helper'
+
+describe Gitlab::Ci::Build::Artifacts::Metadata do
+  def metadata(path = '')
+    described_class.new(metadata_file_path, path)
+  end
+
+  let(:metadata_file_path) do
+    Rails.root + 'spec/fixtures/ci_build_artifacts_metadata.gz'
+  end
+
+  context 'metadata file exists' do
+    describe '#find_entries! empty string' do
+      subject { metadata('').find_entries! }
+
+      it 'matches correct paths' do
+        expect(subject.keys).to contain_exactly 'ci_artifacts.txt',
+                                                'other_artifacts_0.1.2/',
+                                                'rails_sample.jpg',
+                                                'tests_encoding/'
+      end
+
+      it 'matches metadata for every path' do
+        expect(subject.keys.count).to eq 4
+      end
+
+      it 'return Hashes for each metadata' do
+        expect(subject.values).to all(be_kind_of(Hash))
+      end
+    end
+
+    describe '#find_entries! other_artifacts_0.1.2/' do
+      subject { metadata('other_artifacts_0.1.2/').find_entries! }
+
+      it 'matches correct paths' do
+        expect(subject.keys).
+          to contain_exactly 'other_artifacts_0.1.2/',
+                             'other_artifacts_0.1.2/doc_sample.txt',
+                             'other_artifacts_0.1.2/another-subdirectory/'
+      end
+    end
+
+    describe '#find_entries! other_artifacts_0.1.2/another-subdirectory/' do
+      subject { metadata('other_artifacts_0.1.2/another-subdirectory/').find_entries! }
+
+      it 'matches correct paths' do
+        expect(subject.keys).
+          to contain_exactly 'other_artifacts_0.1.2/another-subdirectory/',
+                             'other_artifacts_0.1.2/another-subdirectory/empty_directory/',
+                             'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
+      end
+    end
+
+    describe '#to_entry' do
+      subject { metadata('').to_entry }
+      it { is_expected.to be_an_instance_of(Gitlab::Ci::Build::Artifacts::Metadata::Entry) }
+    end
+
+    describe '#full_version' do
+      subject { metadata('').full_version }
+      it { is_expected.to eq 'GitLab Build Artifacts Metadata 0.0.1' }
+    end
+
+    describe '#version' do
+      subject { metadata('').version }
+      it { is_expected.to eq '0.0.1' }
+    end
+
+    describe '#errors' do
+      subject { metadata('').errors }
+      it { is_expected.to eq({}) }
+    end
+  end
+
+  context 'metadata file does not exist' do
+    let(:metadata_file_path) { '' }
+
+    describe '#find_entries!' do
+      it 'raises error' do
+        expect { metadata.find_entries! }.to raise_error(Errno::ENOENT)
+      end
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/file_spec.rb b/spec/lib/gitlab/diff/file_spec.rb
index c7cdf8691d61b37217bb47dd61439aa47dcb101c..0d9694f2c1304d44023ac55ee33bc3e11d79282e 100644
--- a/spec/lib/gitlab/diff/file_spec.rb
+++ b/spec/lib/gitlab/diff/file_spec.rb
@@ -6,7 +6,7 @@ describe Gitlab::Diff::File, lib: true do
   let(:project) { create(:project) }
   let(:commit) { project.commit(sample_commit.id) }
   let(:diff) { commit.diffs.first }
-  let(:diff_file) { Gitlab::Diff::File.new(diff) }
+  let(:diff_file) { Gitlab::Diff::File.new(diff, [commit.parent, commit]) }
 
   describe :diff_lines do
     let(:diff_lines) { diff_file.diff_lines }
diff --git a/spec/lib/gitlab/diff/highlight_spec.rb b/spec/lib/gitlab/diff/highlight_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b84a57f357adaf15a5d381716da4777bff698c2f
--- /dev/null
+++ b/spec/lib/gitlab/diff/highlight_spec.rb
@@ -0,0 +1,41 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::Highlight, lib: true do
+  include RepoHelpers
+
+  let(:project) { create(:project) }
+  let(:commit) { project.commit(sample_commit.id) }
+  let(:diff) { commit.diffs.first }
+  let(:diff_file) { Gitlab::Diff::File.new(diff, [commit.parent, commit]) }
+
+  describe '#highlight' do
+    let(:diff_lines) { Gitlab::Diff::Highlight.new(diff_file).highlight }
+
+    it 'should return Gitlab::Diff::Line elements' do
+      expect(diff_lines.first).to be_an_instance_of(Gitlab::Diff::Line)
+    end
+
+    it 'should not modify "match" lines' do
+      expect(diff_lines[0].text).to eq('@@ -6,12 +6,18 @@ module Popen')
+      expect(diff_lines[22].text).to eq('@@ -19,6 +25,7 @@ module Popen')
+    end
+
+    it 'should highlight unchanged lines' do
+      code = %Q{ <span id="LC7" class="line">  <span class="k">def</span> <span class="nf">popen</span><span class="p">(</span><span class="n">cmd</span><span class="p">,</span> <span class="n">path</span><span class="o">=</span><span class="kp">nil</span><span class="p">)</span></span>\n}
+
+      expect(diff_lines[2].text).to eq(code)
+    end
+
+    it 'should highlight removed lines' do
+      code = %Q{-<span id="LC9" class="line">      <span class="k">raise</span> <span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+
+      expect(diff_lines[4].text).to eq(code)
+    end
+
+    it 'should highlight added lines' do
+      code = %Q{+<span id="LC9" class="line">      <span class="k">raise</span> <span class="no"><span class='idiff'>RuntimeError</span></span><span class="p"><span class='idiff'>,</span></span><span class='idiff'> </span><span class="s2">&quot;System commands must be given as an array of strings&quot;</span></span>\n}
+
+      expect(diff_lines[5].text).to eq(code)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/inline_diff_marker_spec.rb b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..6f3276a8b533364b367ae2e709c63b52c144d7dc
--- /dev/null
+++ b/spec/lib/gitlab/diff/inline_diff_marker_spec.rb
@@ -0,0 +1,15 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::InlineDiffMarker, lib: true do
+  describe '#inline_diffs' do
+    let(:raw)  { "abc 'def'" }
+    let(:rich) { %{<span class="abc">abc</span><span class="space"> </span><span class="def">&#39;def&#39;</span>} }
+    let(:inline_diffs) { [2..5] }
+
+    let(:subject) { Gitlab::Diff::InlineDiffMarker.new(raw, rich).mark(inline_diffs) }
+
+    it 'marks the inline diffs' do
+      expect(subject).to eq(%{<span class="abc">ab<span class='idiff'>c</span></span><span class="space"><span class='idiff'> </span></span><span class="def"><span class='idiff'>&#39;d</span>ef&#39;</span>})
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/inline_diff_spec.rb b/spec/lib/gitlab/diff/inline_diff_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..056917df8933db1f60b341f6b4794716ba0e36da
--- /dev/null
+++ b/spec/lib/gitlab/diff/inline_diff_spec.rb
@@ -0,0 +1,27 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::InlineDiff, lib: true do
+  describe '#inline_diffs' do
+    let(:diff) do
+      <<eos
+ class Test
+-  def initialize(test = true)
++  def initialize(test = false)
+     @test = test
+   end
+ end
+eos
+    end
+
+    let(:subject) { Gitlab::Diff::InlineDiff.new(diff.lines).inline_diffs }
+
+    it 'finds all inline diffs' do
+      expect(subject[0]).to be_nil
+      expect(subject[1]).to eq([25..27])
+      expect(subject[2]).to eq([25..28])
+      expect(subject[3]).to be_nil
+      expect(subject[4]).to be_nil
+      expect(subject[5]).to be_nil
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/parallel_diff_spec.rb b/spec/lib/gitlab/diff/parallel_diff_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1c5bbc47120477528e573b0d3530bafed7866bb4
--- /dev/null
+++ b/spec/lib/gitlab/diff/parallel_diff_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Gitlab::Diff::ParallelDiff, lib: true do
+  include RepoHelpers
+
+  let(:project) { create(:project) }
+  let(:repository) { project.repository }
+  let(:commit) { project.commit(sample_commit.id) }
+  let(:diffs) { commit.diffs }
+  let(:diff) { diffs.first }
+  let(:diff_refs) { [commit.parent, commit] }
+  let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs) }
+  subject { described_class.new(diff_file) }
+
+  let(:parallel_diff_result_array) { YAML.load_file("#{Rails.root}/spec/fixtures/parallel_diff_result.yml") }
+
+  describe '#parallelize' do
+    it 'should return an array of arrays containing the parsed diff' do
+      expect(subject.parallelize).to match_array(parallel_diff_result_array)
+    end
+  end
+end
diff --git a/spec/lib/gitlab/diff/parser_spec.rb b/spec/lib/gitlab/diff/parser_spec.rb
index ba577bd28e5fa4bb1d0381376bc0a46a55341b3e..fe0dea77909ae6c3ed97c956e7010905fdfd2ceb 100644
--- a/spec/lib/gitlab/diff/parser_spec.rb
+++ b/spec/lib/gitlab/diff/parser_spec.rb
@@ -86,7 +86,7 @@ eos
         it { expect(line.type).to eq(nil) }
         it { expect(line.old_pos).to eq(24) }
         it { expect(line.new_pos).to eq(31) }
-        it { expect(line.text).to eq('       @cmd_output &lt;&lt; stderr.read') }
+        it { expect(line.text).to eq('       @cmd_output << stderr.read') }
       end
     end
   end
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index 9aefec77f6d53882b25467ad7ce3ae95c03d3ca5..6cebcb5009ad99728a9eafa199402e215eaf4977 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -2,8 +2,11 @@ require 'spec_helper'
 
 describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
   let(:project) { create(:project) }
-  let(:source_branch) { OpenStruct.new(ref: 'feature') }
-  let(:target_branch) { OpenStruct.new(ref: 'master') }
+  let(:repository) { OpenStruct.new(id: 1, fork: false) }
+  let(:source_repo) { repository }
+  let(:source_branch) { OpenStruct.new(ref: 'feature', repo: source_repo) }
+  let(:target_repo) { repository }
+  let(:target_branch) { OpenStruct.new(ref: 'master', repo: target_repo) }
   let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') }
   let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
   let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
@@ -125,10 +128,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
   end
 
   describe '#cross_project?' do
-    context 'when source repo is not a fork' do
-      let(:local_repo) { OpenStruct.new(fork: false) }
-      let(:source_branch) { OpenStruct.new(ref: 'feature', repo: local_repo) }
-      let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch)) }
+    context 'when source, and target repositories are the same' do
+      let(:raw_data) { OpenStruct.new(base_data) }
 
       it 'returns false' do
         expect(pull_request.cross_project?).to eq false
@@ -136,9 +137,17 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
     end
 
     context 'when source repo is a fork' do
-      let(:forked_repo) { OpenStruct.new(fork: true) }
-      let(:source_branch) { OpenStruct.new(ref: 'feature', repo: forked_repo) }
-      let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch)) }
+      let(:source_repo) { OpenStruct.new(id: 2, fork: true) }
+      let(:raw_data) { OpenStruct.new(base_data) }
+
+      it 'returns true' do
+        expect(pull_request.cross_project?).to eq true
+      end
+    end
+
+    context 'when target repo is a fork' do
+      let(:target_repo) { OpenStruct.new(id: 2, fork: true) }
+      let(:raw_data) { OpenStruct.new(base_data) }
 
       it 'returns true' do
         expect(pull_request.cross_project?).to eq true
diff --git a/spec/lib/gitlab/github_import/wiki_formatter_spec.rb b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..aed2aa39e3a33ef3d3062fc1fe3a7a4df9becf23
--- /dev/null
+++ b/spec/lib/gitlab/github_import/wiki_formatter_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::WikiFormatter, lib: true do
+  let(:project) do
+    create(:project, namespace: create(:namespace, path: 'gitlabhq'),
+                     import_url: 'https://xxx@github.com/gitlabhq/sample.gitlabhq.git')
+  end
+
+  subject(:wiki) { described_class.new(project)}
+
+  describe '#path_with_namespace' do
+    it 'appends .wiki to project path' do
+      expect(wiki.path_with_namespace).to eq 'gitlabhq/gitlabhq.wiki'
+    end
+  end
+
+  describe '#import_url' do
+    it 'returns URL of the wiki repository' do
+      expect(wiki.import_url).to eq 'https://xxx@github.com/gitlabhq/sample.gitlabhq.wiki.git'
+    end
+  end
+end
diff --git a/spec/lib/gitlab/highlight_spec.rb b/spec/lib/gitlab/highlight_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..1620eb6c60adb507920d56cd7cbf47b8828961a5
--- /dev/null
+++ b/spec/lib/gitlab/highlight_spec.rb
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe Gitlab::Highlight, lib: true do
+  include RepoHelpers
+
+  let(:project) { create(:project) }
+  let(:commit) { project.commit(sample_commit.id) }
+
+  describe '.highlight_lines' do
+    let(:lines) do
+      Gitlab::Highlight.highlight_lines(project.repository, commit.id, 'files/ruby/popen.rb')
+    end
+
+    it 'should properly highlight all the lines' do
+      expect(lines[4]).to eq(%Q{<span id="LC5" class="line">  <span class="kp">extend</span> <span class="nb">self</span></span>\n})
+      expect(lines[21]).to eq(%Q{<span id="LC22" class="line">    <span class="k">unless</span> <span class="no">File</span><span class="p">.</span><span class="nf">directory?</span><span class="p">(</span><span class="n">path</span><span class="p">)</span></span>\n})
+      expect(lines[26]).to eq(%Q{<span id="LC27" class="line">    <span class="vi">@cmd_status</span> <span class="o">=</span> <span class="mi">0</span></span>\n})
+    end
+  end
+
+end
diff --git a/spec/lib/gitlab/inline_diff_spec.rb b/spec/lib/gitlab/inline_diff_spec.rb
deleted file mode 100644
index c690c195112fd8e7fa173c060b3858c24134a0fa..0000000000000000000000000000000000000000
--- a/spec/lib/gitlab/inline_diff_spec.rb
+++ /dev/null
@@ -1,39 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::InlineDiff, lib: true do
-  describe '#processing' do
-    let(:diff) do
-      <<eos
---- a/test.rb
-+++ b/test.rb
-@@ -1,6 +1,6 @@
- class Test
-   def cleanup_string(input)
-     return nil if input.nil?
--    input.sub(/[\\r\\n].+/,'').sub(/\\\\[rn].+/, '').strip
-+    input.to_s.sub(/[\\r\\n].+/,'').sub(/\\\\[rn].+/, '').strip
-   end
- end
-eos
-    end
-
-    let(:expected) do
-      ["--- a/test.rb\n",
-       "+++ b/test.rb\n",
-       "@@ -1,6 +1,6 @@\n",
-       " class Test\n",
-       "   def cleanup_string(input)\n",
-       "     return nil if input.nil?\n",
-       "-    input.#!idiff-start!##!idiff-finish!#sub(/[\\r\\n].+/,'').sub(/\\\\[rn].+/, '').strip\n",
-       "+    input.#!idiff-start!#to_s.#!idiff-finish!#sub(/[\\r\\n].+/,'').sub(/\\\\[rn].+/, '').strip\n",
-       "   end\n",
-       " end\n"]
-    end
-
-    let(:subject) { Gitlab::InlineDiff.processing(diff.lines) }
-
-    it 'should retain backslashes' do
-      expect(subject).to eq(expected)
-    end
-  end
-end
diff --git a/spec/lib/gitlab/ldap/access_spec.rb b/spec/lib/gitlab/ldap/access_spec.rb
index a628d0c01574fb3ad8371f361d46809e6565111c..32a19bf344b2088d12e983b53323bf1a696f4e91 100644
--- a/spec/lib/gitlab/ldap/access_spec.rb
+++ b/spec/lib/gitlab/ldap/access_spec.rb
@@ -13,64 +13,58 @@ describe Gitlab::LDAP::Access, lib: true do
       end
 
       it { is_expected.to be_falsey }
-      
+
       it 'should block user in GitLab' do
         access.allowed?
         expect(user).to be_blocked
+        expect(user).to be_ldap_blocked
       end
     end
 
     context 'when the user is found' do
       before do
-        allow(Gitlab::LDAP::Person).
-          to receive(:find_by_dn).and_return(:ldap_user)
+        allow(Gitlab::LDAP::Person).to receive(:find_by_dn).and_return(:ldap_user)
       end
 
       context 'and the user is disabled via active directory' do
         before do
-          allow(Gitlab::LDAP::Person).
-            to receive(:disabled_via_active_directory?).and_return(true)
+          allow(Gitlab::LDAP::Person).to receive(:disabled_via_active_directory?).and_return(true)
         end
 
         it { is_expected.to be_falsey }
 
-        it "should block user in GitLab" do
+        it 'should block user in GitLab' do
           access.allowed?
           expect(user).to be_blocked
+          expect(user).to be_ldap_blocked
         end
       end
 
       context 'and has no disabled flag in active diretory' do
         before do
-          user.block
-
-          allow(Gitlab::LDAP::Person).
-            to receive(:disabled_via_active_directory?).and_return(false)
+          allow(Gitlab::LDAP::Person).to receive(:disabled_via_active_directory?).and_return(false)
         end
 
         it { is_expected.to be_truthy }
 
         context 'when auto-created users are blocked' do
-
           before do
-            allow_any_instance_of(Gitlab::LDAP::Config).
-              to receive(:block_auto_created_users).and_return(true)
+            user.block
           end
 
-          it "does not unblock user in GitLab" do
+          it 'does not unblock user in GitLab' do
             access.allowed?
             expect(user).to be_blocked
+            expect(user).not_to be_ldap_blocked # this block is handled by omniauth not by our internal logic
           end
         end
 
-        context "when auto-created users are not blocked" do
-
+        context 'when auto-created users are not blocked' do
           before do
-            allow_any_instance_of(Gitlab::LDAP::Config).
-              to receive(:block_auto_created_users).and_return(false)
+            user.ldap_block
           end
 
-          it "should unblock user in GitLab" do
+          it 'should unblock user in GitLab' do
             access.allowed?
             expect(user).not_to be_blocked
           end
@@ -80,8 +74,7 @@ describe Gitlab::LDAP::Access, lib: true do
       context 'without ActiveDirectory enabled' do
         before do
           allow(Gitlab::LDAP::Config).to receive(:enabled?).and_return(true)
-          allow_any_instance_of(Gitlab::LDAP::Config).
-            to receive(:active_directory).and_return(false)
+          allow_any_instance_of(Gitlab::LDAP::Config).to receive(:active_directory).and_return(false)
         end
 
         it { is_expected.to be_truthy }
diff --git a/spec/lib/gitlab/ldap/user_spec.rb b/spec/lib/gitlab/ldap/user_spec.rb
index 1e755259dae4a4bdb85e0f1eccd5ff8ccebefa23..03199a2523e288d77e39e24780c4c1a4fabc284a 100644
--- a/spec/lib/gitlab/ldap/user_spec.rb
+++ b/spec/lib/gitlab/ldap/user_spec.rb
@@ -37,7 +37,7 @@ describe Gitlab::LDAP::User, lib: true do
     end
 
     it "dont marks existing ldap user as changed" do
-      create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain')
+      create(:omniauth_user, email: 'john@example.com', extern_uid: 'my-uid', provider: 'ldapmain', ldap_email: true)
       expect(ldap_user.changed?).to be_falsey
     end
   end
@@ -110,6 +110,32 @@ describe Gitlab::LDAP::User, lib: true do
     end
   end
 
+  describe 'updating email' do
+    context "when LDAP sets an email" do
+      it "has a real email" do
+        expect(ldap_user.gl_user.email).to eq(info[:email])
+      end
+
+      it "has ldap_email set to true" do
+        expect(ldap_user.gl_user.ldap_email?).to be(true)
+      end
+    end
+
+    context "when LDAP doesn't set an email" do
+      before do
+        info.delete(:email)
+      end
+
+      it "has a temp email" do
+        expect(ldap_user.gl_user.temp_oauth_email?).to be(true)
+      end
+
+      it "has ldap_email set to false" do
+        expect(ldap_user.gl_user.ldap_email?).to be(false)
+      end
+    end
+  end
+
   describe 'blocking' do
     def configure_block(value)
       allow_any_instance_of(Gitlab::LDAP::Config).
diff --git a/spec/lib/gitlab/metrics/rack_middleware_spec.rb b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
index 4e6dfc73df2eb8da38f92b64e047942b7648fe15..b99be4e1060025780d2cfedc72f1d93e148b1dcd 100644
--- a/spec/lib/gitlab/metrics/rack_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/rack_middleware_spec.rb
@@ -57,7 +57,7 @@ describe Gitlab::Metrics::RackMiddleware do
 
       middleware.tag_controller(transaction, env)
 
-      expect(transaction.tags[:action]).to eq('TestController#show')
+      expect(transaction.action).to eq('TestController#show')
     end
   end
 end
diff --git a/spec/lib/gitlab/metrics/sampler_spec.rb b/spec/lib/gitlab/metrics/sampler_spec.rb
index 27211350fbee558dcdfa4ed92284aebf03364b9e..38da77adc9f17fe9db4dd60dabb53668b942b1ff 100644
--- a/spec/lib/gitlab/metrics/sampler_spec.rb
+++ b/spec/lib/gitlab/metrics/sampler_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::Metrics::Sampler do
 
   describe '#start' do
     it 'gathers a sample at a given interval' do
-      expect(sampler).to receive(:sleep).with(5)
+      expect(sampler).to receive(:sleep).with(a_kind_of(Numeric))
       expect(sampler).to receive(:sample)
       expect(sampler).to receive(:loop).and_yield
 
@@ -116,4 +116,24 @@ describe Gitlab::Metrics::Sampler do
       sampler.add_metric('cats', value: 10)
     end
   end
+
+  describe '#sleep_interval' do
+    it 'returns a Numeric' do
+      expect(sampler.sleep_interval).to be_a_kind_of(Numeric)
+    end
+
+    # Testing random behaviour is very hard, so treat this test as a basic smoke
+    # test instead of a very accurate behaviour/unit test.
+    it 'does not return the same interval twice in a row' do
+      last = nil
+
+      100.times do
+        interval = sampler.sleep_interval
+
+        expect(interval).to_not eq(last)
+
+        last = interval
+      end
+    end
+  end
 end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index 5882e7d81c7c55059a6d71836ae937c6ecbeaede..e520a9689999a8c297950d94e1011cc4b87a3bae 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -5,22 +5,15 @@ describe Gitlab::Metrics::SidekiqMiddleware do
 
   describe '#call' do
     it 'tracks the transaction' do
-      worker = Class.new.new
+      worker = double(:worker, class: double(:class, name: 'TestWorker'))
+
+      expect(Gitlab::Metrics::Transaction).to receive(:new).
+        with('TestWorker#perform').
+        and_call_original
 
       expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
 
       middleware.call(worker, 'test', :test) { nil }
     end
   end
-
-  describe '#tag_worker' do
-    it 'adds the worker class and action to the transaction' do
-      trans  = Gitlab::Metrics::Transaction.new
-      worker = double(:worker, class: double(:class, name: 'TestWorker'))
-
-      expect(trans).to receive(:add_tag).with(:action, 'TestWorker#perform')
-
-      middleware.tag_worker(trans, worker)
-    end
-  end
 end
diff --git a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
index 05e4fbbeb51226b98167796288965ca1c671dd83..0695c5ce096fca07564bfe53d9397955797cdee6 100644
--- a/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/action_view_spec.rb
@@ -14,19 +14,12 @@ describe Gitlab::Metrics::Subscribers::ActionView do
 
   before do
     allow(subscriber).to receive(:current_transaction).and_return(transaction)
-
-    allow(Gitlab::Metrics).to receive(:last_relative_application_frame).
-      and_return(['app/views/x.html.haml', 4])
   end
 
   describe '#render_template' do
     it 'tracks rendering of a template' do
       values = { duration: 2.1 }
-      tags   = {
-        view: 'app/views/x.html.haml',
-        file: 'app/views/x.html.haml',
-        line: 4
-      }
+      tags   = { view: 'app/views/x.html.haml' }
 
       expect(transaction).to receive(:increment).
         with(:view_duration, 2.1)
diff --git a/spec/lib/gitlab/metrics/transaction_spec.rb b/spec/lib/gitlab/metrics/transaction_spec.rb
index 3a27f8977352c4f3ea6ed644032c3ff5aacf5f18..1d5a51a157ea91b84df9f99cb037ecb42915ec21 100644
--- a/spec/lib/gitlab/metrics/transaction_spec.rb
+++ b/spec/lib/gitlab/metrics/transaction_spec.rb
@@ -11,6 +11,14 @@ describe Gitlab::Metrics::Transaction do
     end
   end
 
+  describe '#allocated_memory' do
+    it 'returns the allocated memory in bytes' do
+      transaction.run { 'a' * 32 }
+
+      expect(transaction.allocated_memory).to be_a_kind_of(Numeric)
+    end
+  end
+
   describe '#run' do
     it 'yields the supplied block' do
       expect { |b| transaction.run(&b) }.to yield_control
@@ -43,8 +51,10 @@ describe Gitlab::Metrics::Transaction do
       transaction.increment(:time, 1)
       transaction.increment(:time, 2)
 
+      values = { duration: 0.0, time: 3, allocated_memory: a_kind_of(Numeric) }
+
       expect(transaction).to receive(:add_metric).
-        with('transactions', { duration: 0.0, time: 3 }, {})
+        with('transactions', values, {})
 
       transaction.track_self
     end
@@ -54,8 +64,14 @@ describe Gitlab::Metrics::Transaction do
     it 'sets a value' do
       transaction.set(:number, 10)
 
+      values = {
+        duration:         0.0,
+        number:           10,
+        allocated_memory: a_kind_of(Numeric)
+      }
+
       expect(transaction).to receive(:add_metric).
-        with('transactions', { duration: 0.0, number: 10 }, {})
+        with('transactions', values, {})
 
       transaction.track_self
     end
@@ -80,8 +96,13 @@ describe Gitlab::Metrics::Transaction do
 
   describe '#track_self' do
     it 'adds a metric for the transaction itself' do
+      values = {
+        duration:         transaction.duration,
+        allocated_memory: a_kind_of(Numeric)
+      }
+
       expect(transaction).to receive(:add_metric).
-        with('transactions', { duration: transaction.duration }, {})
+        with('transactions', values, {})
 
       transaction.track_self
     end
@@ -96,5 +117,22 @@ describe Gitlab::Metrics::Transaction do
 
       transaction.submit
     end
+
+    it 'adds the action as a tag for every metric' do
+      transaction.action = 'Foo#bar'
+      transaction.track_self
+
+      hash = {
+        series:    'rails_transactions',
+        tags:      { action: 'Foo#bar' },
+        values:    { duration: 0.0, allocated_memory: a_kind_of(Numeric) },
+        timestamp: an_instance_of(Fixnum)
+      }
+
+      expect(Gitlab::Metrics).to receive(:submit_metrics).
+        with([hash])
+
+      transaction.submit
+    end
   end
 end
diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb
index c2782f95c8e782d8d4173952cc4c260b460041d5..0ec8a6dc5cb35f4cde6911fd94680d76238c831f 100644
--- a/spec/lib/gitlab/metrics_spec.rb
+++ b/spec/lib/gitlab/metrics_spec.rb
@@ -13,15 +13,6 @@ describe Gitlab::Metrics do
     end
   end
 
-  describe '.last_relative_application_frame' do
-    it 'returns an Array containing a file path and line number' do
-      file, line = described_class.last_relative_application_frame
-
-      expect(line).to eq(__LINE__ - 2)
-      expect(file).to eq('spec/lib/gitlab/metrics_spec.rb')
-    end
-  end
-
   describe '#submit_metrics' do
     it 'prepares and writes the metrics to InfluxDB' do
       connection = double(:connection)
diff --git a/spec/lib/gitlab/note_data_builder_spec.rb b/spec/lib/gitlab/note_data_builder_spec.rb
index 6cbdae737f4ef679c50bb60527bda6ef12834af5..691f36e6cb74eab260512ba516fe7077a36a1a24 100644
--- a/spec/lib/gitlab/note_data_builder_spec.rb
+++ b/spec/lib/gitlab/note_data_builder_spec.rb
@@ -37,7 +37,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
 
     it 'returns the note and issue-specific data' do
       expect(data).to have_key(:issue)
-      expect(data[:issue]).to eq(issue.hook_attrs)
+      expect(data[:issue].except('updated_at')).to eq(issue.hook_attrs.except('updated_at'))
+      expect(data[:issue]['updated_at']).to be > issue.hook_attrs['updated_at']
     end
   end
 
@@ -47,7 +48,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
 
     it 'returns the note and merge request data' do
       expect(data).to have_key(:merge_request)
-      expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+      expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at'))
+      expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at']
     end
   end
 
@@ -57,7 +59,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
 
     it 'returns the note and merge request diff data' do
       expect(data).to have_key(:merge_request)
-      expect(data[:merge_request]).to eq(merge_request.hook_attrs)
+      expect(data[:merge_request].except('updated_at')).to eq(merge_request.hook_attrs.except('updated_at'))
+      expect(data[:merge_request]['updated_at']).to be > merge_request.hook_attrs['updated_at']
     end
   end
 
@@ -67,7 +70,8 @@ describe 'Gitlab::NoteDataBuilder', lib: true do
 
     it 'returns the note and project snippet data' do
       expect(data).to have_key(:snippet)
-      expect(data[:snippet]).to eq(snippet.hook_attrs)
+      expect(data[:snippet].except('updated_at')).to eq(snippet.hook_attrs.except('updated_at'))
+      expect(data[:snippet]['updated_at']).to be > snippet.hook_attrs['updated_at']
     end
   end
 end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 154901a2fbcd44b91332fee867a2ff68fa05e0a8..7289e596ef3da67ef306fbe466af2c8ac76a5a1d 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -40,14 +40,38 @@ describe Notify do
     end
   end
 
+  shared_examples 'an email with X-GitLab headers containing project details' do
+    it 'has X-GitLab-Project* headers' do
+      is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
+      is_expected.to have_header 'X-GitLab-Project-Id', /#{project.id}/
+      is_expected.to have_header 'X-GitLab-Project-Path', /#{project.path_with_namespace}/
+    end
+  end
+
+  shared_examples 'an email with X-GitLab headers containing build details' do
+    it 'has X-GitLab-Build* headers' do
+      is_expected.to have_header 'X-GitLab-Build-Id', /#{build.id}/
+      is_expected.to have_header 'X-GitLab-Build-Ref', /#{build.ref}/
+    end
+  end
+
+  shared_examples 'an email that contains a header with author username' do
+    it 'has X-GitLab-Author header containing author\'s username' do
+      is_expected.to have_header 'X-GitLab-Author', user.username
+    end
+  end
+
   shared_examples 'an email starting a new thread' do |message_id_prefix|
+    include_examples 'an email with X-GitLab headers containing project details'
+
     it 'has a discussion identifier' do
       is_expected.to have_header 'Message-ID',  /<#{message_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
-      is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
     end
   end
 
   shared_examples 'an answer to an existing thread' do |thread_id_prefix|
+    include_examples 'an email with X-GitLab headers containing project details'
+
     it 'has a subject that begins with Re: ' do
       is_expected.to have_subject /^Re: /
     end
@@ -56,7 +80,6 @@ describe Notify do
       is_expected.to have_header 'Message-ID',  /<(.*)@#{Gitlab.config.gitlab.host}>/
       is_expected.to have_header 'References',  /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
       is_expected.to have_header 'In-Reply-To', /<#{thread_id_prefix}(.*)@#{Gitlab.config.gitlab.host}>/
-      is_expected.to have_header 'X-GitLab-Project', /#{project.name}/
     end
   end
 
@@ -104,6 +127,14 @@ describe Notify do
     it { is_expected.to have_body_text /View Commit/ }
   end
 
+  shared_examples 'an unsubscribeable thread' do
+    it { is_expected.to have_body_text /unsubscribe/ }
+  end
+
+  shared_examples "a user cannot unsubscribe through footer link" do
+    it { is_expected.not_to have_body_text /unsubscribe/ }
+  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) }
@@ -115,6 +146,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_behaves_like 'a user cannot unsubscribe through footer link'
 
     it 'contains the password text' do
       is_expected.to have_body_text /Click here to set your password/
@@ -134,7 +166,6 @@ describe Notify do
     end
   end
 
-
   describe 'for users that signed up, the email' do
     let(:example_site_path) { root_path }
     let(:new_user) { create(:user, email: new_user_address, password: "securePassword") }
@@ -144,6 +175,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_behaves_like 'a user cannot unsubscribe through footer link'
 
     it 'should not contain the new user\'s password' do
       is_expected.not_to have_body_text /password/
@@ -157,6 +189,7 @@ describe Notify do
 
     it_behaves_like 'an email sent from GitLab'
     it_behaves_like 'it should not have Gmail Actions links'
+    it_behaves_like 'a user cannot unsubscribe through footer link'
 
     it 'is sent to the new user' do
       is_expected.to deliver_to key.user.email
@@ -181,6 +214,7 @@ describe Notify do
     subject { Notify.new_email_email(email.id) }
 
     it_behaves_like 'it should not have Gmail Actions links'
+    it_behaves_like 'a user cannot unsubscribe through footer link'
 
     it 'is sent to the new user' do
       is_expected.to deliver_to email.user.email
@@ -227,6 +261,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_behaves_like 'an unsubscribeable thread'
 
           it 'has the correct subject' do
             is_expected.to have_subject /#{project.name} \| #{issue.title} \(##{issue.iid}\)/
@@ -253,6 +288,7 @@ describe Notify do
           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_behaves_like "an unsubscribeable thread"
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -283,6 +319,7 @@ describe Notify do
 
           it_behaves_like 'an answer to an existing thread', 'issue'
           it_behaves_like 'it should show Gmail Actions View Issue link'
+          it_behaves_like 'an unsubscribeable thread'
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -319,6 +356,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_behaves_like "an unsubscribeable thread"
 
           it 'has the correct subject' do
             is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -345,6 +383,7 @@ describe Notify 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_behaves_like "an unsubscribeable thread"
 
           it 'contains the description' do
             is_expected.to have_body_text /#{merge_request_with_description.description}/
@@ -357,6 +396,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_behaves_like "an unsubscribeable thread"
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -387,6 +427,7 @@ describe Notify do
 
           it_behaves_like 'an answer to an existing thread', 'merge_request'
           it_behaves_like 'it should show Gmail Actions View Merge request link'
+          it_behaves_like "an unsubscribeable thread"
 
           it 'is sent as the author' do
             sender = subject.header[:from].addrs[0]
@@ -417,6 +458,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_behaves_like "an unsubscribeable thread"
 
           it 'is sent as the merge author' do
             sender = subject.header[:from].addrs[0]
@@ -446,6 +488,7 @@ describe Notify do
 
       it_behaves_like 'an email sent from GitLab'
       it_behaves_like 'it should not have Gmail Actions links'
+      it_behaves_like "a user cannot unsubscribe through footer link"
 
       it 'has the correct subject' do
         is_expected.to have_subject /Project was moved/
@@ -468,6 +511,7 @@ describe Notify do
 
       it_behaves_like 'an email sent from GitLab'
       it_behaves_like 'it should not have Gmail Actions links'
+      it_behaves_like "a user cannot unsubscribe through footer link"
 
       it 'has the correct subject' do
         is_expected.to have_subject /Access to project was granted/
@@ -518,6 +562,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_behaves_like "a user cannot unsubscribe through footer link"
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{commit.title} \(#{commit.short_id}\)/
@@ -538,6 +583,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_behaves_like 'an unsubscribeable thread'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/
@@ -558,6 +604,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_behaves_like 'an unsubscribeable thread'
 
         it 'has the correct subject' do
           is_expected.to have_subject /#{issue.title} \(##{issue.iid}\)/
@@ -579,6 +626,7 @@ describe Notify do
 
     it_behaves_like 'an email sent from GitLab'
     it_behaves_like 'it should not have Gmail Actions links'
+    it_behaves_like "a user cannot unsubscribe through footer link"
 
     it 'has the correct subject' do
       is_expected.to have_subject /Access to group was granted/
@@ -607,6 +655,7 @@ describe Notify do
     subject { ActionMailer::Base.deliveries.last }
 
     it_behaves_like 'an email sent from GitLab'
+    it_behaves_like "a user cannot unsubscribe through footer link"
 
     it 'is sent to the new user' do
       is_expected.to deliver_to 'new-email@mail.com'
@@ -629,6 +678,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -657,6 +709,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -684,6 +739,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -707,6 +765,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -734,6 +795,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -839,6 +903,9 @@ 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_behaves_like "a user cannot unsubscribe through footer link"
+    it_behaves_like 'an email with X-GitLab headers containing project details'
+    it_behaves_like 'an email that contains a header with author username'
 
     it 'is sent as the author' do
       sender = subject.header[:from].addrs[0]
@@ -872,6 +939,15 @@ describe Notify do
 
     subject { Notify.build_success_email(build.id, 'wow@example.com') }
 
+    it_behaves_like 'an email with X-GitLab headers containing build details'
+    it_behaves_like 'an email with X-GitLab headers containing project details' do
+      let(:project) { build.project }
+    end
+
+    it 'has header indicating build status' do
+      is_expected.to have_header 'X-GitLab-Build-Status', 'success'
+    end
+
     it 'has the correct subject' do
       should have_subject /Build success for/
     end
@@ -886,6 +962,15 @@ describe Notify do
 
     subject { Notify.build_fail_email(build.id, 'wow@example.com') }
 
+    it_behaves_like 'an email with X-GitLab headers containing build details'
+    it_behaves_like 'an email with X-GitLab headers containing project details' do
+      let(:project) { build.project }
+    end
+
+    it 'has header indicating build status' do
+      is_expected.to have_header 'X-GitLab-Build-Status', 'failed'
+    end
+
     it 'has the correct subject' do
       should have_subject /Build failed for/
     end
diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb
index 46cab1644c728e1e93274d71a202ab04de37c965..4799bbaa57c89622e1d758dbdf9bcb77dbfb381f 100644
--- a/spec/models/abuse_report_spec.rb
+++ b/spec/models/abuse_report_spec.rb
@@ -26,7 +26,23 @@ RSpec.describe AbuseReport, type: :model do
     it { is_expected.to validate_presence_of(:reporter) }
     it { is_expected.to validate_presence_of(:user) }
     it { is_expected.to validate_presence_of(:message) }
-    it { is_expected.to validate_uniqueness_of(:user_id) }
+    it { is_expected.to validate_uniqueness_of(:user_id).with_message('has already been reported') }
+  end
+
+  describe '#remove_user' do
+    it 'blocks the user' do
+      report = build(:abuse_report)
+
+      allow(report.user).to receive(:destroy)
+
+      expect { report.remove_user }.to change { report.user.blocked? }.to(true)
+    end
+
+    it 'removes the user' do
+      report = build(:abuse_report)
+
+      expect { report.remove_user }.to change { User.count }.by(-1)
+    end
   end
 
   describe '#notify' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 91b250265e65f177389c8e97eded6c210ee78538..f4c588827575ef3f69b4cd9b1b68688d011ae6ac 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -41,6 +41,8 @@
 #  recaptcha_site_key                :string
 #  recaptcha_private_key             :string
 #  metrics_port                      :integer          default(8089)
+#  sentry_enabled                    :boolean          default(FALSE)
+#  sentry_dsn                        :string
 #
 
 require 'spec_helper'
diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb
index e4cac105110ed86113df1bcb0e6dbfc355744f96..f6f84db57e615d69b912976fd38aac6d3038ea6c 100644
--- a/spec/models/broadcast_message_spec.rb
+++ b/spec/models/broadcast_message_spec.rb
@@ -6,7 +6,6 @@
 #  message    :text             not null
 #  starts_at  :datetime
 #  ends_at    :datetime
-#  alert_type :integer
 #  created_at :datetime
 #  updated_at :datetime
 #  color      :string(255)
@@ -16,6 +15,8 @@
 require 'spec_helper'
 
 describe BroadcastMessage, models: true do
+  include ActiveSupport::Testing::TimeHelpers
+
   subject { create(:broadcast_message) }
 
   it { is_expected.to be_valid }
@@ -35,20 +36,79 @@ describe BroadcastMessage, models: true do
     it { is_expected.not_to allow_value('000').for(:font) }
   end
 
-  describe :current do
+  describe '.current' do
     it "should return last message if time match" do
-      broadcast_message = create(:broadcast_message, starts_at: Time.now.yesterday, ends_at: Time.now.tomorrow)
-      expect(BroadcastMessage.current).to eq(broadcast_message)
+      message = create(:broadcast_message)
+
+      expect(BroadcastMessage.current).to eq message
     end
 
     it "should return nil if time not come" do
-      create(:broadcast_message, starts_at: Time.now.tomorrow, ends_at: Time.now + 2.days)
+      create(:broadcast_message, :future)
+
       expect(BroadcastMessage.current).to be_nil
     end
 
     it "should return nil if time has passed" do
-      create(:broadcast_message, starts_at: Time.now - 2.days, ends_at: Time.now.yesterday)
+      create(:broadcast_message, :expired)
+
       expect(BroadcastMessage.current).to be_nil
     end
   end
+
+  describe '#active?' do
+    it 'is truthy when started and not ended' do
+      message = build(:broadcast_message)
+
+      expect(message).to be_active
+    end
+
+    it 'is falsey when ended' do
+      message = build(:broadcast_message, :expired)
+
+      expect(message).not_to be_active
+    end
+
+    it 'is falsey when not started' do
+      message = build(:broadcast_message, :future)
+
+      expect(message).not_to be_active
+    end
+  end
+
+  describe '#started?' do
+    it 'is truthy when starts_at has passed' do
+      message = build(:broadcast_message)
+
+      travel_to(3.days.from_now) do
+        expect(message).to be_started
+      end
+    end
+
+    it 'is falsey when starts_at is in the future' do
+      message = build(:broadcast_message)
+
+      travel_to(3.days.ago) do
+        expect(message).not_to be_started
+      end
+    end
+  end
+
+  describe '#ended?' do
+    it 'is truthy when ends_at has passed' do
+      message = build(:broadcast_message)
+
+      travel_to(3.days.from_now) do
+        expect(message).to be_ended
+      end
+    end
+
+    it 'is falsey when ends_at is in the future' do
+      message = build(:broadcast_message)
+
+      travel_to(3.days.ago) do
+        expect(message).not_to be_ended
+      end
+    end
+  end
 end
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index 1c22e3cb7c405363d30a575ce90178341f345da8..d12b9e65c8239a4ba910a425ba1ccc902f502d24 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -1,28 +1,3 @@
-# == Schema Information
-#
-# Table name: 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
-#  commit_id          :integer
-#  coverage           :float
-#  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
-#
-
 require 'spec_helper'
 
 describe Ci::Build, models: true do
@@ -368,21 +343,75 @@ describe Ci::Build, models: true do
     end
   end
 
-  describe :download_url do
-    subject { build.download_url }
+  describe :artifacts_download_url do
+    subject { build.artifacts_download_url }
 
     it "should be nil if artifact doesn't exist" do
       build.update_attributes(artifacts_file: nil)
       is_expected.to be_nil
     end
 
-    it 'should be nil if artifact exist' do
+    it 'should not be nil if artifact exist' do
       gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
       build.update_attributes(artifacts_file: gif)
       is_expected.to_not be_nil
     end
   end
 
+  describe :artifacts_browse_url do
+    subject { build.artifacts_browse_url }
+
+    it "should be nil if artifacts browser is unsupported" do
+      allow(build).to receive(:artifacts_browser_supported?).and_return(false)
+      is_expected.to be_nil
+    end
+
+    it 'should not be nil if artifacts browser is supported' do
+      allow(build).to receive(:artifacts_browser_supported?).and_return(true)
+      is_expected.to_not be_nil
+    end
+  end
+
+  describe :artifacts? do
+    subject { build.artifacts? }
+
+    context 'artifacts archive does not exist' do
+      before { build.update_attributes(artifacts_file: nil) }
+      it { is_expected.to be_falsy }
+    end
+
+    context 'artifacts archive exists' do
+      before do
+        gif = fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif')
+        build.update_attributes(artifacts_file: gif)
+      end
+
+      it { is_expected.to be_truthy }
+    end
+  end
+
+
+  describe :artifacts_browser_supported? do
+    subject { build.artifacts_browser_supported? }
+    context 'artifacts metadata does not exist' do
+      it { is_expected.to be_falsy }
+    end
+
+    context 'artifacts archive is a zip file and metadata exists' do
+      before do
+        fixture_dir = Rails.root + 'spec/fixtures/'
+        archive = fixture_file_upload(fixture_dir + 'ci_build_artifacts.zip',
+                                      'application/zip')
+        metadata = fixture_file_upload(fixture_dir + 'ci_build_artifacts_metadata.gz',
+                                       'application/x-gzip')
+        build.update_attributes(artifacts_file: archive)
+        build.update_attributes(artifacts_metadata: metadata)
+      end
+
+      it { is_expected.to be_truthy }
+    end
+  end
+
   describe :repo_url do
     let(:build) { FactoryGirl.create :ci_build }
     let(:project) { build.project }
@@ -397,6 +426,30 @@ describe Ci::Build, models: true do
     it { is_expected.to include(project.web_url[7..-1]) }
   end
 
+  describe :depends_on_builds do
+    let!(:build) { FactoryGirl.create :ci_build, commit: commit, name: 'build', stage_idx: 0, stage: 'build' }
+    let!(:rspec_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rspec', stage_idx: 1, stage: 'test' }
+    let!(:rubocop_test) { FactoryGirl.create :ci_build, commit: commit, name: 'rubocop', stage_idx: 1, stage: 'test' }
+    let!(:staging) { FactoryGirl.create :ci_build, commit: commit, name: 'staging', stage_idx: 2, stage: 'deploy' }
+
+    it 'to have no dependents if this is first build' do
+      expect(build.depends_on_builds).to be_empty
+    end
+
+    it 'to have one dependent if this is test' do
+      expect(rspec_test.depends_on_builds.map(&:id)).to contain_exactly(build.id)
+    end
+
+    it 'to have all builds from build and test stage if this is last' do
+      expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, rspec_test.id, rubocop_test.id)
+    end
+
+    it 'to have retried builds instead the original ones' do
+      retried_rspec = Ci::Build.retry(rspec_test)
+      expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, retried_rspec.id, rubocop_test.id)
+    end
+  end
+
   def create_mr(build, commit, factory: :merge_request, created_at: Time.now)
     FactoryGirl.create(factory,
                        source_project_id: commit.gl_project_id,
diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..5afe042e154ab9bc03e2f68908b1b6d88d403a7a
--- /dev/null
+++ b/spec/models/identity_spec.rb
@@ -0,0 +1,38 @@
+# == Schema Information
+#
+# Table name: identities
+#
+#  id         :integer          not null, primary key
+#  extern_uid :string(255)
+#  provider   :string(255)
+#  user_id    :integer
+#  created_at :datetime
+#  updated_at :datetime
+#
+
+require 'spec_helper'
+
+RSpec.describe Identity, models: true do
+
+  describe 'relations' do
+    it { is_expected.to belong_to(:user) }
+  end
+
+  describe 'fields' do
+    it { is_expected.to respond_to(:provider) }
+    it { is_expected.to respond_to(:extern_uid) }
+  end
+
+  describe '#is_ldap?' do
+    let(:ldap_identity) { create(:identity, provider: 'ldapmain') }
+    let(:other_identity) { create(:identity, provider: 'twitter') }
+
+    it 'returns true if it is a ldap identity' do
+      expect(ldap_identity.ldap?).to be_truthy
+    end
+
+    it 'returns false if it is not a ldap identity' do
+      expect(other_identity.ldap?).to be_falsey
+    end
+  end
+end
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 151a29e974b23d5b12119f83abeb507d557eabcc..9182b42661d9969865b6c535b9a72c75c6f4e9d3 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -178,6 +178,30 @@ describe Note, models: true do
     end
   end
 
+  describe "cross_reference_not_visible_for?" do
+    let(:private_user)    { create(:user) }
+    let(:private_project) { create(:project, namespace: private_user.namespace).tap { |p| p.team << [private_user, :master] } }
+    let(:private_issue)   { create(:issue, project: private_project) }
+
+    let(:ext_proj)  { create(:project, :public) }
+    let(:ext_issue) { create(:issue, project: ext_proj) }
+
+    let(:note) do
+      create :note,
+        noteable: ext_issue, project: ext_proj,
+        note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
+        system: true
+    end
+
+    it "returns true" do
+      expect(note.cross_reference_not_visible_for?(ext_issue.author)).to be_truthy
+    end
+
+    it "returns false" do
+      expect(note.cross_reference_not_visible_for?(private_user)).to be_falsy
+    end
+  end
+
   describe "set_award!" do
     let(:issue) { create :issue }
 
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 876b927eaeaca8b9f9b60208bf71cca64ab7549e..a2085df5bcd6aa79c588fc2cc63a7bf558809e4f 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -36,6 +36,13 @@ describe ProjectWiki, models: true do
     end
   end
 
+  describe "#wiki_base_path" do
+    it "returns the wiki base path" do
+      wiki_base_path = "/#{project.path_with_namespace}/wikis"
+      expect(subject.wiki_base_path).to eq(wiki_base_path)
+    end
+  end
+
   describe "#wiki" do
     it "contains a Gollum::Wiki instance" do
       expect(subject.wiki).to be_a Gollum::Wiki
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3cd63b2b0e8a4e6ef6099a4767455e89381529ff..0bef68e2885898d0612adc7580801b09d793c88a 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -569,27 +569,39 @@ describe User, models: true do
     end
   end
 
-  describe :ldap_user? do
-    it "is true if provider name starts with ldap" do
-      user = create(:omniauth_user, provider: 'ldapmain')
-      expect( user.ldap_user? ).to be_truthy
-    end
+  context 'ldap synchronized user' do
+    describe :ldap_user? do
+      it 'is true if provider name starts with ldap' do
+        user = create(:omniauth_user, provider: 'ldapmain')
+        expect(user.ldap_user?).to be_truthy
+      end
 
-    it "is false for other providers" do
-      user = create(:omniauth_user, provider: 'other-provider')
-      expect( user.ldap_user? ).to be_falsey
+      it 'is false for other providers' do
+        user = create(:omniauth_user, provider: 'other-provider')
+        expect(user.ldap_user?).to be_falsey
+      end
+
+      it 'is false if no extern_uid is provided' do
+        user = create(:omniauth_user, extern_uid: nil)
+        expect(user.ldap_user?).to be_falsey
+      end
     end
 
-    it "is false if no extern_uid is provided" do
-      user = create(:omniauth_user, extern_uid: nil)
-      expect( user.ldap_user? ).to be_falsey
+    describe :ldap_identity do
+      it 'returns ldap identity' do
+        user = create :omniauth_user
+        expect(user.ldap_identity.provider).not_to be_empty
+      end
     end
-  end
 
-  describe :ldap_identity do
-    it "returns ldap identity" do
-      user = create :omniauth_user
-      expect(user.ldap_identity.provider).not_to be_empty
+    describe '#ldap_block' do
+      let(:user) { create(:omniauth_user, provider: 'ldapmain', name: 'John Smith') }
+
+      it 'blocks user flaging the action caming from ldap' do
+        user.ldap_block
+        expect(user.blocked?).to be_truthy
+        expect(user.ldap_blocked?).to be_truthy
+      end
     end
   end
 
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8c9f5a382b7004db977e2b8edfa33e1c10c92a49
--- /dev/null
+++ b/spec/requests/api/builds_spec.rb
@@ -0,0 +1,172 @@
+require 'spec_helper'
+
+describe API::API, api: true  do
+  include ApiHelpers
+
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:developer) { create(:project_member, user: user, project: project, access_level: ProjectMember::DEVELOPER) }
+  let!(:reporter) { create(:project_member, user: user2, project: project, access_level: ProjectMember::REPORTER) }
+  let(:commit) { create(:ci_commit, project: project)}
+  let(:build) { create(:ci_build, commit: commit) }
+  let(:build_with_trace) { create(:ci_build_with_trace, commit: commit) }
+  let(:build_canceled) { create(:ci_build, :canceled, commit: commit) }
+
+  describe 'GET /projects/:id/builds ' do
+    context 'authorized user' do
+      it 'should return project builds' do
+        get api("/projects/#{project.id}/builds", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+      end
+
+      it 'should filter project with one scope element' do
+        get api("/projects/#{project.id}/builds?scope=pending", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+      end
+
+      it 'should filter project with array of scope elements' do
+        get api("/projects/#{project.id}/builds?scope[0]=pending&scope[1]=running", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+      end
+
+      it 'should respond 400 when scope contains invalid state' do
+        get api("/projects/#{project.id}/builds?scope[0]=pending&scope[1]=unknown_status", user)
+
+        expect(response.status).to eq(400)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return project builds' do
+        get api("/projects/#{project.id}/builds")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'GET /projects/:id/repository/commits/:sha/builds' do
+    context 'authorized user' do
+      it 'should return project builds for specific commit' do
+        project.ensure_ci_commit(commit.sha)
+        get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_an Array
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return project builds' do
+        project.ensure_ci_commit(commit.sha)
+        get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'GET /projects/:id/builds/:build_id' do
+    context 'authorized user' do
+      it 'should return specific build data' do
+        get api("/projects/#{project.id}/builds/#{build.id}", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response['name']).to eq('test')
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return specific build data' do
+        get api("/projects/#{project.id}/builds/#{build.id}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'GET /projects/:id/builds/:build_id/trace' do
+    context 'authorized user' do
+      it 'should return specific build trace' do
+        get api("/projects/#{project.id}/builds/#{build_with_trace.id}/trace", user)
+
+        expect(response.status).to eq(200)
+        expect(response.body).to eq(build_with_trace.trace)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return specific build trace' do
+        get api("/projects/#{project.id}/builds/#{build_with_trace.id}/trace")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/builds/:build_id/cancel' do
+    context 'authorized user' do
+      context 'user with :manage_builds persmission' do
+        it 'should cancel running or pending build' do
+          post api("/projects/#{project.id}/builds/#{build.id}/cancel", user)
+
+          expect(response.status).to eq(201)
+          expect(project.builds.first.status).to eq('canceled')
+        end
+      end
+
+      context 'user without :manage_builds permission' do
+        it 'should not cancel build' do
+          post api("/projects/#{project.id}/builds/#{build.id}/cancel", user2)
+
+          expect(response.status).to eq(403)
+        end
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not cancel build' do
+        post api("/projects/#{project.id}/builds/#{build.id}/cancel")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/builds/:build_id/retry' do
+    context 'authorized user' do
+      context 'user with :manage_builds persmission' do
+        it 'should retry non-running build' do
+          post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user)
+
+          expect(response.status).to eq(201)
+          expect(project.builds.first.status).to eq('canceled')
+          expect(json_response['status']).to eq('pending')
+        end
+      end
+
+      context 'user without :manage_builds permission' do
+        it 'should not retry build' do
+          post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry", user2)
+
+          expect(response.status).to eq(403)
+        end
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not retry build' do
+        post api("/projects/#{project.id}/builds/#{build_canceled.id}/retry")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+end
diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb
index a28607bd2400a1f7f8f5035b5547bb3bd0bf253a..21482fc1070b40123e0baef34703349d3d740e29 100644
--- a/spec/requests/api/commit_status_spec.rb
+++ b/spec/requests/api/commit_status_spec.rb
@@ -1,6 +1,6 @@
 require 'spec_helper'
 
-describe API::API, api: true do
+describe API::CommitStatus, api: true do
   include ApiHelpers
   let(:user) { create(:user) }
   let(:user2) { create(:user) }
@@ -12,6 +12,10 @@ describe API::API, api: true do
   let(:commit_status) { create(:commit_status, commit: ci_commit) }
 
   describe "GET /projects/:id/repository/commits/:sha/statuses" do
+    it_behaves_like 'a paginated resources' do
+      let(:request) { get api("/projects/#{project.id}/repository/commits/#{commit.id}/statuses", user) }
+    end
+
     context "reporter user" do
       let(:statuses_id) { json_response.map { |status| status['id'] } }
 
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 8b177af4689b09b63ff434c891d7d4feebdfa358..39f9a06fe1ba0b2bf2106da97048bd75b99c74ec 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -10,9 +10,32 @@ describe API::API, api: true  do
   let!(:issue_note) { create(:note, noteable: issue, project: project, author: user) }
   let!(:merge_request_note) { create(:note, noteable: merge_request, project: project, author: user) }
   let!(:snippet_note) { create(:note, noteable: snippet, project: project, author: user) }
+
+  # For testing the cross-reference of a private issue in a public issue
+  let(:private_user)    { create(:user) }
+  let(:private_project) do
+    create(:project, namespace: private_user.namespace).
+    tap { |p| p.team << [private_user, :master] }
+  end
+  let(:private_issue)    { create(:issue, project: private_project) }
+
+  let(:ext_proj)  { create(:project, :public) }
+  let(:ext_issue) { create(:issue, project: ext_proj) }
+
+  let!(:cross_reference_note) do
+    create :note,
+    noteable: ext_issue, project: ext_proj,
+    note: "mentioned in issue #{private_issue.to_reference(ext_proj)}",
+    system: true
+  end
+
   before { project.team << [user, :reporter] }
 
   describe "GET /projects/:id/noteable/:noteable_id/notes" do
+    it_behaves_like 'a paginated resources' do
+      let(:request) { get api("/projects/#{project.id}/issues/#{issue.id}/notes", user) }
+    end
+
     context "when noteable is an Issue" do
       it "should return an array of issue notes" do
         get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
@@ -25,6 +48,24 @@ describe API::API, api: true  do
         get api("/projects/#{project.id}/issues/123/notes", user)
         expect(response.status).to eq(404)
       end
+
+      context "that references a private issue" do
+        it "should return an empty array" do
+          get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
+          expect(response.status).to eq(200)
+          expect(json_response).to be_an Array
+          expect(json_response).to be_empty
+        end
+
+        context "and current user can view the note" do
+          it "should return an empty array" do
+            get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user)
+            expect(response.status).to eq(200)
+            expect(json_response).to be_an Array
+            expect(json_response.first['body']).to eq(cross_reference_note.note)
+          end
+        end
+      end
     end
 
     context "when noteable is a Snippet" do
@@ -68,6 +109,21 @@ describe API::API, api: true  do
         get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user)
         expect(response.status).to eq(404)
       end
+
+      context "that references a private issue" do
+        it "should return a 404 error" do
+          get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user)
+          expect(response.status).to eq(404)
+        end
+
+        context "and current user can view the note" do
+          it "should return an issue note by id" do
+            get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user)
+            expect(response.status).to eq(200)
+            expect(json_response['body']).to eq(cross_reference_note.note)
+          end
+        end
+      end
     end
 
     context "when noteable is a Snippet" do
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 6f4c336b66ca65e37c6bc79c80f59986993022ea..2a310f3834dc2d062273be150c5a8a057baa39ed 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -90,6 +90,29 @@ describe API::API, api: true  do
         end
       end
 
+      context 'and using the visibility filter' do
+        it 'should filter based on private visibility param' do
+          get api('/projects', user), { visibility: 'private' }
+          expect(response.status).to eq(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PRIVATE).count)
+        end
+
+        it 'should filter based on internal visibility param' do
+          get api('/projects', user), { visibility: 'internal' }
+          expect(response.status).to eq(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::INTERNAL).count)
+        end
+
+        it 'should filter based on public visibility param' do
+          get api('/projects', user), { visibility: 'public' }
+          expect(response.status).to eq(200)
+          expect(json_response).to be_an Array
+          expect(json_response.length).to eq(user.namespace.projects.where(visibility_level: Gitlab::VisibilityLevel::PUBLIC).count)
+        end
+      end
+
       context 'and using sorting' do
         before do
           project2
diff --git a/spec/requests/api/triggers_spec.rb b/spec/requests/api/triggers_spec.rb
index 314bd7ddc59bfa3a8aae2853449ba282d945242f..2a86b60bc4d1b5b1f3cc9821dc1b289eb9df0111 100644
--- a/spec/requests/api/triggers_spec.rb
+++ b/spec/requests/api/triggers_spec.rb
@@ -3,11 +3,19 @@ require 'spec_helper'
 describe API::API do
   include ApiHelpers
 
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:trigger_token) { 'secure_token' }
+  let!(:trigger_token_2) { 'secure_token_2' }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
+  let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) }
+  let!(:trigger) { create(:ci_trigger, project: project, token: trigger_token) }
+  let!(:trigger2) { create(:ci_trigger, project: project, token: trigger_token_2) }
+  let!(:trigger_request) { create(:ci_trigger_request, trigger: trigger, created_at: '2015-01-01 12:13:14') }
+
   describe 'POST /projects/:project_id/trigger' do
-    let!(:trigger_token) { 'secure token' }
-    let!(:project) { FactoryGirl.create(:project) }
-    let!(:project2) { FactoryGirl.create(:empty_project) }
-    let!(:trigger) { FactoryGirl.create(:ci_trigger, project: project, token: trigger_token) }
+    let!(:project2) { create(:empty_project) }
     let(:options) do
       {
         token: trigger_token
@@ -77,4 +85,127 @@ describe API::API do
       end
     end
   end
+
+  describe 'GET /projects/:id/triggers' do
+    context 'authenticated user with valid permissions' do
+      it 'should return list of triggers' do
+        get api("/projects/#{project.id}/triggers", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_a(Array)
+        expect(json_response[0]).to have_key('token')
+      end
+    end
+
+    context 'authenticated user with invalid permissions' do
+      it 'should not return triggers list' do
+        get api("/projects/#{project.id}/triggers", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthenticated user' do
+      it 'should not return triggers list' do
+        get api("/projects/#{project.id}/triggers")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'GET /projects/:id/triggers/:token' do
+    context 'authenticated user with valid permissions' do
+      it 'should return trigger details' do
+        get api("/projects/#{project.id}/triggers/#{trigger.token}", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_a(Hash)
+      end
+
+      it 'should respond with 404 Not Found if requesting non-existing trigger' do
+        get api("/projects/#{project.id}/triggers/abcdef012345", user)
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'authenticated user with invalid permissions' do
+      it 'should not return triggers list' do
+        get api("/projects/#{project.id}/triggers/#{trigger.token}", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthenticated user' do
+      it 'should not return triggers list' do
+        get api("/projects/#{project.id}/triggers/#{trigger.token}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/triggers' do
+    context 'authenticated user with valid permissions' do
+      it 'should create trigger' do
+        expect do
+          post api("/projects/#{project.id}/triggers", user)
+        end.to change{project.triggers.count}.by(1)
+
+        expect(response.status).to eq(201)
+        expect(json_response).to be_a(Hash)
+      end
+    end
+
+    context 'authenticated user with invalid permissions' do
+      it 'should not create trigger' do
+        post api("/projects/#{project.id}/triggers", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthenticated user' do
+      it 'should not create trigger' do
+        post api("/projects/#{project.id}/triggers")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'DELETE /projects/:id/triggers/:token' do
+    context 'authenticated user with valid permissions' do
+      it 'should delete trigger' do
+        expect do
+          delete api("/projects/#{project.id}/triggers/#{trigger.token}", user)
+        end.to change{project.triggers.count}.by(-1)
+        expect(response.status).to eq(200)
+      end
+
+      it 'should respond with 404 Not Found if requesting non-existing trigger' do
+        delete api("/projects/#{project.id}/triggers/abcdef012345", user)
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'authenticated user with invalid permissions' do
+      it 'should not delete trigger' do
+        delete api("/projects/#{project.id}/triggers/#{trigger.token}", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthenticated user' do
+      it 'should not delete trigger' do
+        delete api("/projects/#{project.id}/triggers/#{trigger.token}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
 end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 4f278551d0741b8cf7001fbf2f39570c2ef5a5fb..b82c5c7685f4eb56115a9bec3125e35df166de0d 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -8,6 +8,8 @@ describe API::API, api: true  do
   let(:key)   { create(:key, user: user) }
   let(:email)   { create(:email, user: user) }
   let(:omniauth_user) { create(:omniauth_user) }
+  let(:ldap_user) { create(:omniauth_user, provider: 'ldapmain') }
+  let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
 
   describe "GET /users" do
     context "when unauthenticated" do
@@ -783,6 +785,12 @@ describe API::API, api: true  do
       expect(user.reload.state).to eq('blocked')
     end
 
+    it 'should not re-block ldap blocked users' do
+      put api("/users/#{ldap_blocked_user.id}/block", admin)
+      expect(response.status).to eq(403)
+      expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
+    end
+
     it 'should not be available for non admin users' do
       put api("/users/#{user.id}/block", user)
       expect(response.status).to eq(403)
@@ -797,7 +805,9 @@ describe API::API, api: true  do
   end
 
   describe 'PUT /user/:id/unblock' do
+    let(:blocked_user)  { create(:user, state: 'blocked') }
     before { admin }
+
     it 'should unblock existing user' do
       put api("/users/#{user.id}/unblock", admin)
       expect(response.status).to eq(200)
@@ -805,12 +815,15 @@ describe API::API, api: true  do
     end
 
     it 'should unblock a blocked user' do
-      put api("/users/#{user.id}/block", admin)
-      expect(response.status).to eq(200)
-      expect(user.reload.state).to eq('blocked')
-      put api("/users/#{user.id}/unblock", admin)
+      put api("/users/#{blocked_user.id}/unblock", admin)
       expect(response.status).to eq(200)
-      expect(user.reload.state).to eq('active')
+      expect(blocked_user.reload.state).to eq('active')
+    end
+
+    it 'should not unblock ldap blocked users' do
+      put api("/users/#{ldap_blocked_user.id}/unblock", admin)
+      expect(response.status).to eq(403)
+      expect(ldap_blocked_user.reload.state).to eq('ldap_blocked')
     end
 
     it 'should not be available for non admin users' do
diff --git a/spec/requests/api/variables_spec.rb b/spec/requests/api/variables_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..9744729ba0c4620166c4564c2b85a1c4e5c0c9e2
--- /dev/null
+++ b/spec/requests/api/variables_spec.rb
@@ -0,0 +1,182 @@
+require 'spec_helper'
+
+describe API::API, api: true do
+  include ApiHelpers
+
+  let(:user) { create(:user) }
+  let(:user2) { create(:user) }
+  let!(:project) { create(:project, creator_id: user.id) }
+  let!(:master) { create(:project_member, user: user, project: project, access_level: ProjectMember::MASTER) }
+  let!(:developer) { create(:project_member, user: user2, project: project, access_level: ProjectMember::DEVELOPER) }
+  let!(:variable) { create(:ci_variable, project: project) }
+
+  describe 'GET /projects/:id/variables' do
+    context 'authorized user with proper permissions' do
+      it 'should return project variables' do
+        get api("/projects/#{project.id}/variables", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response).to be_a(Array)
+      end
+    end
+
+    context 'authorized user with invalid permissions' do
+      it 'should not return project variables' do
+        get api("/projects/#{project.id}/variables", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return project variables' do
+        get api("/projects/#{project.id}/variables")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'GET /projects/:id/variables/:key' do
+    context 'authorized user with proper permissions' do
+      it 'should return project variable details' do
+        get api("/projects/#{project.id}/variables/#{variable.key}", user)
+
+        expect(response.status).to eq(200)
+        expect(json_response['value']).to eq(variable.value)
+      end
+
+      it 'should respond with 404 Not Found if requesting non-existing variable' do
+        get api("/projects/#{project.id}/variables/non_existing_variable", user)
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'authorized user with invalid permissions' do
+      it 'should not return project variable details' do
+        get api("/projects/#{project.id}/variables/#{variable.key}", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not return project variable details' do
+        get api("/projects/#{project.id}/variables/#{variable.key}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'POST /projects/:id/variables' do
+    context 'authorized user with proper permissions' do
+      it 'should create variable' do
+        expect do
+          post api("/projects/#{project.id}/variables", user), key: 'TEST_VARIABLE_2', value: 'VALUE_2'
+        end.to change{project.variables.count}.by(1)
+
+        expect(response.status).to eq(201)
+        expect(json_response['key']).to eq('TEST_VARIABLE_2')
+        expect(json_response['value']).to eq('VALUE_2')
+      end
+
+      it 'should not allow to duplicate variable key' do
+        expect do
+          post api("/projects/#{project.id}/variables", user), key: variable.key, value: 'VALUE_2'
+        end.to change{project.variables.count}.by(0)
+
+        expect(response.status).to eq(400)
+      end
+    end
+
+    context 'authorized user with invalid permissions' do
+      it 'should not create variable' do
+        post api("/projects/#{project.id}/variables", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not create variable' do
+        post api("/projects/#{project.id}/variables")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'PUT /projects/:id/variables/:key' do
+    context 'authorized user with proper permissions' do
+      it 'should update variable data' do
+        initial_variable = project.variables.first
+        value_before = initial_variable.value
+
+        put api("/projects/#{project.id}/variables/#{variable.key}", user), value: 'VALUE_1_UP'
+
+        updated_variable = project.variables.first
+
+        expect(response.status).to eq(200)
+        expect(value_before).to eq(variable.value)
+        expect(updated_variable.value).to eq('VALUE_1_UP')
+      end
+
+      it 'should responde with 404 Not Found if requesting non-existing variable' do
+        put api("/projects/#{project.id}/variables/non_existing_variable", user)
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'authorized user with invalid permissions' do
+      it 'should not update variable' do
+        put api("/projects/#{project.id}/variables/#{variable.key}", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not update variable' do
+        put api("/projects/#{project.id}/variables/#{variable.key}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+
+  describe 'DELETE /projects/:id/variables/:key' do
+    context 'authorized user with proper permissions' do
+      it 'should delete variable' do
+        expect do
+          delete api("/projects/#{project.id}/variables/#{variable.key}", user)
+        end.to change{project.variables.count}.by(-1)
+        expect(response.status).to eq(200)
+      end
+
+      it 'should responde with 404 Not Found if requesting non-existing variable' do
+        delete api("/projects/#{project.id}/variables/non_existing_variable", user)
+
+        expect(response.status).to eq(404)
+      end
+    end
+
+    context 'authorized user with invalid permissions' do
+      it 'should not delete variable' do
+        delete api("/projects/#{project.id}/variables/#{variable.key}", user2)
+
+        expect(response.status).to eq(403)
+      end
+    end
+
+    context 'unauthorized user' do
+      it 'should not delete variable' do
+        delete api("/projects/#{project.id}/variables/#{variable.key}")
+
+        expect(response.status).to eq(401)
+      end
+    end
+  end
+end
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index c27e87c4acce8dc5aed72444ad19b9f57ba6e590..eec927102aa4bbd8dd53e8854f66d1be72a9f426 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -101,6 +101,18 @@ describe Ci::API::API do
           { "key" => "TRIGGER_KEY", "value" => "TRIGGER_VALUE", "public" => false },
         ])
       end
+
+      it "returns dependent builds" do
+        commit = FactoryGirl.create(:ci_commit, project: project)
+        commit.create_builds('master', false, nil, nil)
+        commit.builds.where(stage: 'test').each(&:success)
+
+        post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin }
+
+        expect(response.status).to eq(201)
+        expect(json_response["depends_on_builds"].count).to eq(2)
+        expect(json_response["depends_on_builds"][0]["name"]).to eq("rspec")
+      end
     end
 
     describe "PUT /builds/:id" do
@@ -210,6 +222,52 @@ describe Ci::API::API do
             end
           end
 
+          context 'should post artifacts file and metadata file' do
+            let!(:artifacts) { file_upload }
+            let!(:metadata) { file_upload2 }
+
+            let(:stored_artifacts_file) { build.reload.artifacts_file.file }
+            let(:stored_metadata_file) { build.reload.artifacts_metadata.file }
+
+            before do
+              build.run!
+              post(post_url, post_data, headers_with_token)
+            end
+
+            context 'post data accelerated by workhorse is correct' do
+              let(:post_data) do
+                { 'file.path' => artifacts.path,
+                  'file.name' => artifacts.original_filename,
+                  'metadata.path' => metadata.path,
+                  'metadata.name' => metadata.original_filename }
+              end
+
+              it 'responds with valid status' do
+                expect(response.status).to eq(201)
+              end
+
+              it 'stores artifacts and artifacts metadata' do
+                expect(stored_artifacts_file.original_filename).to eq(artifacts.original_filename)
+                expect(stored_metadata_file.original_filename).to eq(metadata.original_filename)
+              end
+            end
+
+            context 'no artifacts file in post data' do
+              let(:post_data) do
+                { 'metadata' => metadata }
+              end
+
+              it 'is expected to respond with bad request' do
+                expect(response.status).to eq(400)
+              end
+
+              it 'does not store metadata' do
+                expect(stored_metadata_file).to be_nil
+              end
+            end
+          end
+
+
           context "should fail to post too large artifact" do
             before do
               build.run!
diff --git a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
index 449cecaa7896803fc65055a491f41ff8980d61ce..de9fed2b7dd590f662177bf8a915f98a3cdee0a6 100644
--- a/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
+++ b/spec/services/merge_requests/merge_when_build_succeeds_service_spec.rb
@@ -6,7 +6,7 @@ describe MergeRequests::MergeWhenBuildSucceedsService do
 
   let(:mr_merge_if_green_enabled) do
     create(:merge_request, merge_when_build_succeeds: true, merge_user: user,
-                           source_branch: "source_branch", target_branch: project.default_branch,
+                           source_branch: "master", target_branch: 'feature',
                            source_project: project, target_project: project, state: "opened")
   end
 
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 6d219f35895bf13af9ff59b6e9bfe646089756b5..2d0b5df422462fc6398d88e5e3a683dc16c6b223 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -227,7 +227,7 @@ describe NotificationService, services: true do
     end
 
     describe :reassigned_issue do
-      it 'should email new assignee' do
+      it 'emails new assignee' do
         notification.reassigned_issue(issue, @u_disabled)
 
         should_email(issue.assignee)
@@ -238,6 +238,62 @@ describe NotificationService, services: true do
         should_not_email(@u_participating)
         should_not_email(@u_disabled)
       end
+
+      it 'emails previous assignee even if he has the "on mention" notif level' do
+        issue.update_attribute(:assignee, @u_mentioned)
+        issue.update_attributes(assignee: @u_watcher)
+        notification.reassigned_issue(issue, @u_disabled)
+
+        should_email(@u_mentioned)
+        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
+
+      it 'emails new assignee even if he has the "on mention" notif level' do
+        issue.update_attributes(assignee: @u_mentioned)
+        notification.reassigned_issue(issue, @u_disabled)
+
+        expect(issue.assignee).to be @u_mentioned
+        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
+
+      it 'emails new assignee' do
+        issue.update_attribute(:assignee, @u_mentioned)
+        notification.reassigned_issue(issue, @u_disabled)
+
+        expect(issue.assignee).to be @u_mentioned
+        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
+
+      it 'does not email new assignee if they are the current user' do
+        issue.update_attribute(:assignee, @u_mentioned)
+        notification.reassigned_issue(issue, @u_mentioned)
+
+        expect(issue.assignee).to be @u_mentioned
+        should_email(@u_watcher)
+        should_email(@u_participant_mentioned)
+        should_email(@subscriber)
+        should_not_email(issue.assignee)
+        should_not_email(@unsubscriber)
+        should_not_email(@u_participating)
+        should_not_email(@u_disabled)
+      end
     end
 
     describe :close_issue do
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index 5d0b18558b152c81414356dbe52d20e5eab216e4..e43903dbd3cefb7e068d2c11d42618c786858bbf 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -32,6 +32,7 @@ describe Projects::CreateService, services: true do
 
       it { expect(@project).to be_valid }
       it { expect(@project.owner).to eq(@user) }
+      it { expect(@project.team.masters).to include(@user) }
       it { expect(@project.namespace).to eq(@user.namespace) }
     end
 
diff --git a/spec/services/repair_ldap_blocked_user_service_spec.rb b/spec/services/repair_ldap_blocked_user_service_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..ce7d1455975409772068666a304866fb68402e84
--- /dev/null
+++ b/spec/services/repair_ldap_blocked_user_service_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe RepairLdapBlockedUserService, services: true do
+  let(:user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') }
+  let(:identity) { user.ldap_identity }
+  subject(:service) { RepairLdapBlockedUserService.new(user) }
+
+  describe '#execute' do
+    it 'change to normal block after destroying last ldap identity' do
+      identity.destroy
+      service.execute
+
+      expect(user.reload).not_to be_ldap_blocked
+    end
+
+    it 'change to normal block after changing last ldap identity to another provider' do
+      identity.update_attribute(:provider, 'twitter')
+      service.execute
+
+      expect(user.reload).not_to be_ldap_blocked
+    end
+  end
+end
diff --git a/spec/services/system_hooks_service_spec.rb b/spec/services/system_hooks_service_spec.rb
index 4455ae7b321e38317027a1befa1b49850cf4c312..fef211ded50901b0cb266eec118b568e4b09d754 100644
--- a/spec/services/system_hooks_service_spec.rb
+++ b/spec/services/system_hooks_service_spec.rb
@@ -9,54 +9,54 @@ describe SystemHooksService, services: true do
   let(:group_member)  { create(:group_member) }
 
   context 'event data' do
-    it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) }
-    it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id) }
+    it { expect(event_data(user, :create)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
+    it { expect(event_data(user, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :email, :user_id, :username) }
     it { expect(event_data(project, :create)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
     it { expect(event_data(project, :destroy)).to include(:event_name, :name, :created_at, :updated_at, :path, :project_id, :owner_name, :owner_email, :project_visibility) }
-    it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
-    it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_email, :access_level, :project_visibility) }
+    it { expect(event_data(project_member, :create)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
+    it { expect(event_data(project_member, :destroy)).to include(:event_name, :created_at, :updated_at, :project_name, :project_path, :project_path_with_namespace, :project_id, :user_name, :user_username, :user_email, :user_id, :access_level, :project_visibility) }
     it { expect(event_data(key, :create)).to include(:username, :key, :id) }
     it { expect(event_data(key, :destroy)).to include(:username, :key, :id) }
 
     it do
       project.old_path_with_namespace = 'renamed_from_path'
       expect(event_data(project, :rename)).to include(
-        :event_name, :name, :created_at, :updated_at, :path, :project_id, 
-        :owner_name, :owner_email, :project_visibility, 
+        :event_name, :name, :created_at, :updated_at, :path, :project_id,
+        :owner_name, :owner_email, :project_visibility,
         :old_path_with_namespace
-      ) 
+      )
     end
     it do
       project.old_path_with_namespace = 'transfered_from_path'
       expect(event_data(project, :transfer)).to include(
-        :event_name, :name, :created_at, :updated_at, :path, :project_id, 
-        :owner_name, :owner_email, :project_visibility, 
+        :event_name, :name, :created_at, :updated_at, :path, :project_id,
+        :owner_name, :owner_email, :project_visibility,
         :old_path_with_namespace
-      ) 
+      )
     end
 
     it do
       expect(event_data(group, :create)).to include(
-        :event_name, :name, :created_at, :updated_at, :path, :group_id, 
+        :event_name, :name, :created_at, :updated_at, :path, :group_id,
         :owner_name, :owner_email
       )
     end
     it do
       expect(event_data(group, :destroy)).to include(
-        :event_name, :name, :created_at, :updated_at, :path, :group_id, 
+        :event_name, :name, :created_at, :updated_at, :path, :group_id,
         :owner_name, :owner_email
       )
     end
     it do
       expect(event_data(group_member, :create)).to include(
-        :event_name, :created_at, :updated_at, :group_name, :group_path, 
-        :group_id, :user_id, :user_name, :user_email, :group_access
+        :event_name, :created_at, :updated_at, :group_name, :group_path,
+        :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
       )
     end
     it do
       expect(event_data(group_member, :destroy)).to include(
-        :event_name, :created_at, :updated_at, :group_name, :group_path, 
-        :group_id, :user_id, :user_name, :user_email, :group_access
+        :event_name, :created_at, :updated_at, :group_name, :group_path,
+        :group_id, :user_id, :user_username, :user_name, :user_email, :group_access
       )
     end
   end
diff --git a/spec/support/api/pagination_shared_examples.rb b/spec/support/api/pagination_shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..352a6eeec79fb7dcf84dc5bc8687529ff6026227
--- /dev/null
+++ b/spec/support/api/pagination_shared_examples.rb
@@ -0,0 +1,20 @@
+# Specs for paginated resources.
+#
+# Requires an API request:
+#   let(:request) { get api("/projects/#{project.id}/repository/branches", user) }
+shared_examples 'a paginated resources' do
+  before do
+    # Fires the request
+    request
+  end
+
+  it 'has pagination headers' do
+    expect(response.headers).to include('X-Total')
+    expect(response.headers).to include('X-Total-Pages')
+    expect(response.headers).to include('X-Per-Page')
+    expect(response.headers).to include('X-Page')
+    expect(response.headers).to include('X-Next-Page')
+    expect(response.headers).to include('X-Prev-Page')
+    expect(response.headers).to include('Link')
+  end
+end
diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml
index 3482145404eb06fff24a3aeba0732a3d1de94804..a5b256bd3ec8e54b4f90ad59689abdc90e8f8196 100644
--- a/spec/support/gitlab_stubs/gitlab_ci.yml
+++ b/spec/support/gitlab_stubs/gitlab_ci.yml
@@ -36,8 +36,8 @@ staging:
   script: "cap deploy stating"
   type: deploy
   tags: 
-    - capistrano
-    - debian
+    - ruby
+    - mysql
   except:
     - stable
 
@@ -47,8 +47,8 @@ production:
     - cap deploy production
     - cap notify
   tags: 
-    - capistrano
-    - debian
+    - ruby
+    - mysql
   only:
     - master
     - /^deploy-.*$/
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index 5d97fdd4882aa1c25632874a844645554a540c84..73c6792b65fea9542d4f0dafc58435dace017c71 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -28,6 +28,10 @@ class MarkdownFeature
     end
   end
 
+  def project_wiki
+    @project_wiki ||= ProjectWiki.new(project, user)
+  end
+
   def issue
     @issue ||= create(:issue, project: project)
   end
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index b251e7f8f2346df7846648b821d97f913b1beb01..1d52489e804939b9520cc990c3a9e8724a0240a5 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -66,6 +66,24 @@ module MarkdownMatchers
     end
   end
 
+  # GollumTagsFilter
+  matcher :parse_gollum_tags do
+    def have_image(src)
+      have_css("img[src$='#{src}']")
+    end
+
+    set_default_markdown_messages
+
+    match do |actual|
+      expect(actual).to have_link('linked-resource', href: 'linked-resource')
+      expect(actual).to have_link('link-text', href: 'linked-resource')
+      expect(actual).to have_link('http://example.com', href: 'http://example.com')
+      expect(actual).to have_link('link-text', href: 'http://example.com/pdfs/gollum.pdf')
+      expect(actual).to have_image('/gitlabhq/wikis/images/example.jpg')
+      expect(actual).to have_image('http://example.com/images/example.jpg')
+    end
+  end
+
   # UserReferenceFilter
   matcher :reference_users do
     set_default_markdown_messages
diff --git a/vendor/assets/javascripts/autosize.js b/vendor/assets/javascripts/autosize.js
new file mode 100755
index 0000000000000000000000000000000000000000..cfa49e72c50e70138ccb89d2d80e1bcbed1e7b6b
--- /dev/null
+++ b/vendor/assets/javascripts/autosize.js
@@ -0,0 +1,243 @@
+/*!
+	Autosize 3.0.14
+	license: MIT
+	http://www.jacklmoore.com/autosize
+*/
+(function (global, factory) {
+	if (typeof define === 'function' && define.amd) {
+		define(['exports', 'module'], factory);
+	} else if (typeof exports !== 'undefined' && typeof module !== 'undefined') {
+		factory(exports, module);
+	} else {
+		var mod = {
+			exports: {}
+		};
+		factory(mod.exports, mod);
+		global.autosize = mod.exports;
+	}
+})(this, function (exports, module) {
+	'use strict';
+
+	var set = typeof Set === 'function' ? new Set() : (function () {
+		var list = [];
+
+		return {
+			has: function has(key) {
+				return Boolean(list.indexOf(key) > -1);
+			},
+			add: function add(key) {
+				list.push(key);
+			},
+			'delete': function _delete(key) {
+				list.splice(list.indexOf(key), 1);
+			} };
+	})();
+
+	function assign(ta) {
+		var _ref = arguments[1] === undefined ? {} : arguments[1];
+
+		var _ref$setOverflowX = _ref.setOverflowX;
+		var setOverflowX = _ref$setOverflowX === undefined ? true : _ref$setOverflowX;
+		var _ref$setOverflowY = _ref.setOverflowY;
+		var setOverflowY = _ref$setOverflowY === undefined ? true : _ref$setOverflowY;
+
+		if (!ta || !ta.nodeName || ta.nodeName !== 'TEXTAREA' || set.has(ta)) return;
+
+		var heightOffset = null;
+		var overflowY = null;
+		var clientWidth = ta.clientWidth;
+
+		function init() {
+			var style = window.getComputedStyle(ta, null);
+
+			overflowY = style.overflowY;
+
+			if (style.resize === 'vertical') {
+				ta.style.resize = 'none';
+			} else if (style.resize === 'both') {
+				ta.style.resize = 'horizontal';
+			}
+
+			if (style.boxSizing === 'content-box') {
+				heightOffset = -(parseFloat(style.paddingTop) + parseFloat(style.paddingBottom));
+			} else {
+				heightOffset = parseFloat(style.borderTopWidth) + parseFloat(style.borderBottomWidth);
+			}
+			// Fix when a textarea is not on document body and heightOffset is Not a Number
+			if (isNaN(heightOffset)) {
+				heightOffset = 0;
+			}
+
+			update();
+		}
+
+		function changeOverflow(value) {
+			{
+				// Chrome/Safari-specific fix:
+				// When the textarea y-overflow is hidden, Chrome/Safari do not reflow the text to account for the space
+				// made available by removing the scrollbar. The following forces the necessary text reflow.
+				var width = ta.style.width;
+				ta.style.width = '0px';
+				// Force reflow:
+				/* jshint ignore:start */
+				ta.offsetWidth;
+				/* jshint ignore:end */
+				ta.style.width = width;
+			}
+
+			overflowY = value;
+
+			if (setOverflowY) {
+				ta.style.overflowY = value;
+			}
+
+			resize();
+		}
+
+		function resize() {
+			var htmlTop = window.pageYOffset;
+			var bodyTop = document.body.scrollTop;
+			var originalHeight = ta.style.height;
+
+			ta.style.height = 'auto';
+
+			var endHeight = ta.scrollHeight + heightOffset;
+
+			if (ta.scrollHeight === 0) {
+				// If the scrollHeight is 0, then the element probably has display:none or is detached from the DOM.
+				ta.style.height = originalHeight;
+				return;
+			}
+
+			ta.style.height = endHeight + 'px';
+
+			// used to check if an update is actually necessary on window.resize
+			clientWidth = ta.clientWidth;
+
+			// prevents scroll-position jumping
+			document.documentElement.scrollTop = htmlTop;
+			document.body.scrollTop = bodyTop;
+		}
+
+		function update() {
+			var startHeight = ta.style.height;
+
+			resize();
+
+			var style = window.getComputedStyle(ta, null);
+
+			if (style.height !== ta.style.height) {
+				if (overflowY !== 'visible') {
+					changeOverflow('visible');
+				}
+			} else {
+				if (overflowY !== 'hidden') {
+					changeOverflow('hidden');
+				}
+			}
+
+			if (startHeight !== ta.style.height) {
+				var evt = document.createEvent('Event');
+				evt.initEvent('autosize:resized', true, false);
+				ta.dispatchEvent(evt);
+			}
+		}
+
+		var pageResize = function pageResize() {
+			if (ta.clientWidth !== clientWidth) {
+				update();
+			}
+		};
+
+		var destroy = (function (style) {
+			window.removeEventListener('resize', pageResize, false);
+			ta.removeEventListener('input', update, false);
+			ta.removeEventListener('keyup', update, false);
+			ta.removeEventListener('autosize:destroy', destroy, false);
+			ta.removeEventListener('autosize:update', update, false);
+			set['delete'](ta);
+
+			Object.keys(style).forEach(function (key) {
+				ta.style[key] = style[key];
+			});
+		}).bind(ta, {
+			height: ta.style.height,
+			resize: ta.style.resize,
+			overflowY: ta.style.overflowY,
+			overflowX: ta.style.overflowX,
+			wordWrap: ta.style.wordWrap });
+
+		ta.addEventListener('autosize:destroy', destroy, false);
+
+		// IE9 does not fire onpropertychange or oninput for deletions,
+		// so binding to onkeyup to catch most of those events.
+		// There is no way that I know of to detect something like 'cut' in IE9.
+		if ('onpropertychange' in ta && 'oninput' in ta) {
+			ta.addEventListener('keyup', update, false);
+		}
+
+		window.addEventListener('resize', pageResize, false);
+		ta.addEventListener('input', update, false);
+		ta.addEventListener('autosize:update', update, false);
+		set.add(ta);
+
+		if (setOverflowX) {
+			ta.style.overflowX = 'hidden';
+			ta.style.wordWrap = 'break-word';
+		}
+
+		init();
+	}
+
+	function destroy(ta) {
+		if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
+		var evt = document.createEvent('Event');
+		evt.initEvent('autosize:destroy', true, false);
+		ta.dispatchEvent(evt);
+	}
+
+	function update(ta) {
+		if (!(ta && ta.nodeName && ta.nodeName === 'TEXTAREA')) return;
+		var evt = document.createEvent('Event');
+		evt.initEvent('autosize:update', true, false);
+		ta.dispatchEvent(evt);
+	}
+
+	var autosize = null;
+
+	// Do nothing in Node.js environment and IE8 (or lower)
+	if (typeof window === 'undefined' || typeof window.getComputedStyle !== 'function') {
+		autosize = function (el) {
+			return el;
+		};
+		autosize.destroy = function (el) {
+			return el;
+		};
+		autosize.update = function (el) {
+			return el;
+		};
+	} else {
+		autosize = function (el, options) {
+			if (el) {
+				Array.prototype.forEach.call(el.length ? el : [el], function (x) {
+					return assign(x, options);
+				});
+			}
+			return el;
+		};
+		autosize.destroy = function (el) {
+			if (el) {
+				Array.prototype.forEach.call(el.length ? el : [el], destroy);
+			}
+			return el;
+		};
+		autosize.update = function (el) {
+			if (el) {
+				Array.prototype.forEach.call(el.length ? el : [el], update);
+			}
+			return el;
+		};
+	}
+
+	module.exports = autosize;
+});
\ No newline at end of file
diff --git a/vendor/assets/javascripts/latinise.js b/vendor/assets/javascripts/latinise.js
new file mode 100644
index 0000000000000000000000000000000000000000..da37966b28a1aab29183e9fd2daeca8fc82e2111
--- /dev/null
+++ b/vendor/assets/javascripts/latinise.js
@@ -0,0 +1,11 @@
+// Converting text to basic latin (aka removing accents)
+//
+// Based on: http://semplicewebsites.com/removing-accents-javascript
+//
+var Latinise = {
+  map: {"Á":"A","Ă":"A","Ắ":"A","Ặ":"A","Ằ":"A","Ẳ":"A","Ẵ":"A","Ǎ":"A","Â":"A","Ấ":"A","Ậ":"A","Ầ":"A","Ẩ":"A","Ẫ":"A","Ä":"A","Ǟ":"A","Ȧ":"A","Ǡ":"A","Ạ":"A","Ȁ":"A","À":"A","Ả":"A","Ȃ":"A","Ā":"A","Ą":"A","Å":"A","Ǻ":"A","Ḁ":"A","Ⱥ":"A","Ã":"A","Ꜳ":"AA","Æ":"AE","Ǽ":"AE","Ǣ":"AE","Ꜵ":"AO","Ꜷ":"AU","Ꜹ":"AV","Ꜻ":"AV","Ꜽ":"AY","Ḃ":"B","Ḅ":"B","Ɓ":"B","Ḇ":"B","Ƀ":"B","Ƃ":"B","Ć":"C","Č":"C","Ç":"C","Ḉ":"C","Ĉ":"C","Ċ":"C","Ƈ":"C","Ȼ":"C","Ď":"D","Ḑ":"D","Ḓ":"D","Ḋ":"D","Ḍ":"D","Ɗ":"D","Ḏ":"D","Dz":"D","Dž":"D","Đ":"D","Ƌ":"D","DZ":"DZ","DŽ":"DZ","É":"E","Ĕ":"E","Ě":"E","Ȩ":"E","Ḝ":"E","Ê":"E","Ế":"E","Ệ":"E","Ề":"E","Ể":"E","Ễ":"E","Ḙ":"E","Ë":"E","Ė":"E","Ẹ":"E","Ȅ":"E","È":"E","Ẻ":"E","Ȇ":"E","Ē":"E","Ḗ":"E","Ḕ":"E","Ę":"E","Ɇ":"E","Ẽ":"E","Ḛ":"E","Ꝫ":"ET","Ḟ":"F","Ƒ":"F","Ǵ":"G","Ğ":"G","Ǧ":"G","Ģ":"G","Ĝ":"G","Ġ":"G","Ɠ":"G","Ḡ":"G","Ǥ":"G","Ḫ":"H","Ȟ":"H","Ḩ":"H","Ĥ":"H","Ⱨ":"H","Ḧ":"H","Ḣ":"H","Ḥ":"H","Ħ":"H","Í":"I","Ĭ":"I","Ǐ":"I","Î":"I","Ï":"I","Ḯ":"I","İ":"I","Ị":"I","Ȉ":"I","Ì":"I","Ỉ":"I","Ȋ":"I","Ī":"I","Į":"I","Ɨ":"I","Ĩ":"I","Ḭ":"I","Ꝺ":"D","Ꝼ":"F","Ᵹ":"G","Ꞃ":"R","Ꞅ":"S","Ꞇ":"T","Ꝭ":"IS","Ĵ":"J","Ɉ":"J","Ḱ":"K","Ǩ":"K","Ķ":"K","Ⱪ":"K","Ꝃ":"K","Ḳ":"K","Ƙ":"K","Ḵ":"K","Ꝁ":"K","Ꝅ":"K","Ĺ":"L","Ƚ":"L","Ľ":"L","Ļ":"L","Ḽ":"L","Ḷ":"L","Ḹ":"L","Ⱡ":"L","Ꝉ":"L","Ḻ":"L","Ŀ":"L","Ɫ":"L","Lj":"L","Ł":"L","LJ":"LJ","Ḿ":"M","Ṁ":"M","Ṃ":"M","Ɱ":"M","Ń":"N","Ň":"N","Ņ":"N","Ṋ":"N","Ṅ":"N","Ṇ":"N","Ǹ":"N","Ɲ":"N","Ṉ":"N","Ƞ":"N","Nj":"N","Ñ":"N","NJ":"NJ","Ó":"O","Ŏ":"O","Ǒ":"O","Ô":"O","Ố":"O","Ộ":"O","Ồ":"O","Ổ":"O","Ỗ":"O","Ö":"O","Ȫ":"O","Ȯ":"O","Ȱ":"O","Ọ":"O","Ő":"O","Ȍ":"O","Ò":"O","Ỏ":"O","Ơ":"O","Ớ":"O","Ợ":"O","Ờ":"O","Ở":"O","Ỡ":"O","Ȏ":"O","Ꝋ":"O","Ꝍ":"O","Ō":"O","Ṓ":"O","Ṑ":"O","Ɵ":"O","Ǫ":"O","Ǭ":"O","Ø":"O","Ǿ":"O","Õ":"O","Ṍ":"O","Ṏ":"O","Ȭ":"O","Ƣ":"OI","Ꝏ":"OO","Ɛ":"E","Ɔ":"O","Ȣ":"OU","Ṕ":"P","Ṗ":"P","Ꝓ":"P","Ƥ":"P","Ꝕ":"P","Ᵽ":"P","Ꝑ":"P","Ꝙ":"Q","Ꝗ":"Q","Ŕ":"R","Ř":"R","Ŗ":"R","Ṙ":"R","Ṛ":"R","Ṝ":"R","Ȑ":"R","Ȓ":"R","Ṟ":"R","Ɍ":"R","Ɽ":"R","Ꜿ":"C","Ǝ":"E","Ś":"S","Ṥ":"S","Š":"S","Ṧ":"S","Ş":"S","Ŝ":"S","Ș":"S","Ṡ":"S","Ṣ":"S","Ṩ":"S","ẞ":"SS","Ť":"T","Ţ":"T","Ṱ":"T","Ț":"T","Ⱦ":"T","Ṫ":"T","Ṭ":"T","Ƭ":"T","Ṯ":"T","Ʈ":"T","Ŧ":"T","Ɐ":"A","Ꞁ":"L","Ɯ":"M","Ʌ":"V","Ꜩ":"TZ","Ú":"U","Ŭ":"U","Ǔ":"U","Û":"U","Ṷ":"U","Ü":"U","Ǘ":"U","Ǚ":"U","Ǜ":"U","Ǖ":"U","Ṳ":"U","Ụ":"U","Ű":"U","Ȕ":"U","Ù":"U","Ủ":"U","Ư":"U","Ứ":"U","Ự":"U","Ừ":"U","Ử":"U","Ữ":"U","Ȗ":"U","Ū":"U","Ṻ":"U","Ų":"U","Ů":"U","Ũ":"U","Ṹ":"U","Ṵ":"U","Ꝟ":"V","Ṿ":"V","Ʋ":"V","Ṽ":"V","Ꝡ":"VY","Ẃ":"W","Ŵ":"W","Ẅ":"W","Ẇ":"W","Ẉ":"W","Ẁ":"W","Ⱳ":"W","Ẍ":"X","Ẋ":"X","Ý":"Y","Ŷ":"Y","Ÿ":"Y","Ẏ":"Y","Ỵ":"Y","Ỳ":"Y","Ƴ":"Y","Ỷ":"Y","Ỿ":"Y","Ȳ":"Y","Ɏ":"Y","Ỹ":"Y","Ź":"Z","Ž":"Z","Ẑ":"Z","Ⱬ":"Z","Ż":"Z","Ẓ":"Z","Ȥ":"Z","Ẕ":"Z","Ƶ":"Z","IJ":"IJ","Œ":"OE","ᴀ":"A","ᴁ":"AE","ʙ":"B","ᴃ":"B","ᴄ":"C","ᴅ":"D","ᴇ":"E","ꜰ":"F","ɢ":"G","ʛ":"G","ʜ":"H","ɪ":"I","ʁ":"R","ᴊ":"J","ᴋ":"K","ʟ":"L","ᴌ":"L","ᴍ":"M","ɴ":"N","ᴏ":"O","ɶ":"OE","ᴐ":"O","ᴕ":"OU","ᴘ":"P","ʀ":"R","ᴎ":"N","ᴙ":"R","ꜱ":"S","ᴛ":"T","ⱻ":"E","ᴚ":"R","ᴜ":"U","ᴠ":"V","ᴡ":"W","ʏ":"Y","ᴢ":"Z","á":"a","ă":"a","ắ":"a","ặ":"a","ằ":"a","ẳ":"a","ẵ":"a","ǎ":"a","â":"a","ấ":"a","ậ":"a","ầ":"a","ẩ":"a","ẫ":"a","ä":"a","ǟ":"a","ȧ":"a","ǡ":"a","ạ":"a","ȁ":"a","à":"a","ả":"a","ȃ":"a","ā":"a","ą":"a","ᶏ":"a","ẚ":"a","å":"a","ǻ":"a","ḁ":"a","ⱥ":"a","ã":"a","ꜳ":"aa","æ":"ae","ǽ":"ae","ǣ":"ae","ꜵ":"ao","ꜷ":"au","ꜹ":"av","ꜻ":"av","ꜽ":"ay","ḃ":"b","ḅ":"b","ɓ":"b","ḇ":"b","ᵬ":"b","ᶀ":"b","ƀ":"b","ƃ":"b","ɵ":"o","ć":"c","č":"c","ç":"c","ḉ":"c","ĉ":"c","ɕ":"c","ċ":"c","ƈ":"c","ȼ":"c","ď":"d","ḑ":"d","ḓ":"d","ȡ":"d","ḋ":"d","ḍ":"d","ɗ":"d","ᶑ":"d","ḏ":"d","ᵭ":"d","ᶁ":"d","đ":"d","ɖ":"d","ƌ":"d","ı":"i","ȷ":"j","ɟ":"j","ʄ":"j","dz":"dz","dž":"dz","é":"e","ĕ":"e","ě":"e","ȩ":"e","ḝ":"e","ê":"e","ế":"e","ệ":"e","ề":"e","ể":"e","ễ":"e","ḙ":"e","ë":"e","ė":"e","ẹ":"e","ȅ":"e","è":"e","ẻ":"e","ȇ":"e","ē":"e","ḗ":"e","ḕ":"e","ⱸ":"e","ę":"e","ᶒ":"e","ɇ":"e","ẽ":"e","ḛ":"e","ꝫ":"et","ḟ":"f","ƒ":"f","ᵮ":"f","ᶂ":"f","ǵ":"g","ğ":"g","ǧ":"g","ģ":"g","ĝ":"g","ġ":"g","ɠ":"g","ḡ":"g","ᶃ":"g","ǥ":"g","ḫ":"h","ȟ":"h","ḩ":"h","ĥ":"h","ⱨ":"h","ḧ":"h","ḣ":"h","ḥ":"h","ɦ":"h","ẖ":"h","ħ":"h","ƕ":"hv","í":"i","ĭ":"i","ǐ":"i","î":"i","ï":"i","ḯ":"i","ị":"i","ȉ":"i","ì":"i","ỉ":"i","ȋ":"i","ī":"i","į":"i","ᶖ":"i","ɨ":"i","ĩ":"i","ḭ":"i","ꝺ":"d","ꝼ":"f","ᵹ":"g","ꞃ":"r","ꞅ":"s","ꞇ":"t","ꝭ":"is","ǰ":"j","ĵ":"j","ʝ":"j","ɉ":"j","ḱ":"k","ǩ":"k","ķ":"k","ⱪ":"k","ꝃ":"k","ḳ":"k","ƙ":"k","ḵ":"k","ᶄ":"k","ꝁ":"k","ꝅ":"k","ĺ":"l","ƚ":"l","ɬ":"l","ľ":"l","ļ":"l","ḽ":"l","ȴ":"l","ḷ":"l","ḹ":"l","ⱡ":"l","ꝉ":"l","ḻ":"l","ŀ":"l","ɫ":"l","ᶅ":"l","ɭ":"l","ł":"l","lj":"lj","ſ":"s","ẜ":"s","ẛ":"s","ẝ":"s","ḿ":"m","ṁ":"m","ṃ":"m","ɱ":"m","ᵯ":"m","ᶆ":"m","ń":"n","ň":"n","ņ":"n","ṋ":"n","ȵ":"n","ṅ":"n","ṇ":"n","ǹ":"n","ɲ":"n","ṉ":"n","ƞ":"n","ᵰ":"n","ᶇ":"n","ɳ":"n","ñ":"n","nj":"nj","ó":"o","ŏ":"o","ǒ":"o","ô":"o","ố":"o","ộ":"o","ồ":"o","ổ":"o","ỗ":"o","ö":"o","ȫ":"o","ȯ":"o","ȱ":"o","ọ":"o","ő":"o","ȍ":"o","ò":"o","ỏ":"o","ơ":"o","ớ":"o","ợ":"o","ờ":"o","ở":"o","ỡ":"o","ȏ":"o","ꝋ":"o","ꝍ":"o","ⱺ":"o","ō":"o","ṓ":"o","ṑ":"o","ǫ":"o","ǭ":"o","ø":"o","ǿ":"o","õ":"o","ṍ":"o","ṏ":"o","ȭ":"o","ƣ":"oi","ꝏ":"oo","ɛ":"e","ᶓ":"e","ɔ":"o","ᶗ":"o","ȣ":"ou","ṕ":"p","ṗ":"p","ꝓ":"p","ƥ":"p","ᵱ":"p","ᶈ":"p","ꝕ":"p","ᵽ":"p","ꝑ":"p","ꝙ":"q","ʠ":"q","ɋ":"q","ꝗ":"q","ŕ":"r","ř":"r","ŗ":"r","ṙ":"r","ṛ":"r","ṝ":"r","ȑ":"r","ɾ":"r","ᵳ":"r","ȓ":"r","ṟ":"r","ɼ":"r","ᵲ":"r","ᶉ":"r","ɍ":"r","ɽ":"r","ↄ":"c","ꜿ":"c","ɘ":"e","ɿ":"r","ś":"s","ṥ":"s","š":"s","ṧ":"s","ş":"s","ŝ":"s","ș":"s","ṡ":"s","ṣ":"s","ṩ":"s","ʂ":"s","ᵴ":"s","ᶊ":"s","ȿ":"s","ɡ":"g","ß":"ss","ᴑ":"o","ᴓ":"o","ᴝ":"u","ť":"t","ţ":"t","ṱ":"t","ț":"t","ȶ":"t","ẗ":"t","ⱦ":"t","ṫ":"t","ṭ":"t","ƭ":"t","ṯ":"t","ᵵ":"t","ƫ":"t","ʈ":"t","ŧ":"t","ᵺ":"th","ɐ":"a","ᴂ":"ae","ǝ":"e","ᵷ":"g","ɥ":"h","ʮ":"h","ʯ":"h","ᴉ":"i","ʞ":"k","ꞁ":"l","ɯ":"m","ɰ":"m","ᴔ":"oe","ɹ":"r","ɻ":"r","ɺ":"r","ⱹ":"r","ʇ":"t","ʌ":"v","ʍ":"w","ʎ":"y","ꜩ":"tz","ú":"u","ŭ":"u","ǔ":"u","û":"u","ṷ":"u","ü":"u","ǘ":"u","ǚ":"u","ǜ":"u","ǖ":"u","ṳ":"u","ụ":"u","ű":"u","ȕ":"u","ù":"u","ủ":"u","ư":"u","ứ":"u","ự":"u","ừ":"u","ử":"u","ữ":"u","ȗ":"u","ū":"u","ṻ":"u","ų":"u","ᶙ":"u","ů":"u","ũ":"u","ṹ":"u","ṵ":"u","ᵫ":"ue","ꝸ":"um","ⱴ":"v","ꝟ":"v","ṿ":"v","ʋ":"v","ᶌ":"v","ⱱ":"v","ṽ":"v","ꝡ":"vy","ẃ":"w","ŵ":"w","ẅ":"w","ẇ":"w","ẉ":"w","ẁ":"w","ⱳ":"w","ẘ":"w","ẍ":"x","ẋ":"x","ᶍ":"x","ý":"y","ŷ":"y","ÿ":"y","ẏ":"y","ỵ":"y","ỳ":"y","ƴ":"y","ỷ":"y","ỿ":"y","ȳ":"y","ẙ":"y","ɏ":"y","ỹ":"y","ź":"z","ž":"z","ẑ":"z","ʑ":"z","ⱬ":"z","ż":"z","ẓ":"z","ȥ":"z","ẕ":"z","ᵶ":"z","ᶎ":"z","ʐ":"z","ƶ":"z","ɀ":"z","ff":"ff","ffi":"ffi","ffl":"ffl","fi":"fi","fl":"fl","ij":"ij","œ":"oe","st":"st","ₐ":"a","ₑ":"e","ᵢ":"i","ⱼ":"j","ₒ":"o","ᵣ":"r","ᵤ":"u","ᵥ":"v","ₓ":"x"}
+};
+
+String.prototype.latinise = function() {
+  return this.replace(/[^A-Za-z0-9]/g, function(x) { return Latinise.map[x] || x; });
+};